Lots of progress on indefinite lengths; refactor decoder to get of weird semi-recursion
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index c6a348b..17dc1f2 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		0F60E27B2164705400483952 /* qcbor_decode_malloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */; };
 		E73B5756216071900080D658 /* bstrwrap_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B5755216071900080D658 /* bstrwrap_tests.c */; };
 		E73B57592161CA690080D658 /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
 		E73B575E2161CA7C0080D658 /* half_precision_test.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* half_precision_test.c */; };
@@ -32,6 +33,7 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		0F60E27A2164705400483952 /* qcbor_decode_malloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qcbor_decode_malloc.c; sourceTree = "<group>"; };
 		E73B5754216071900080D658 /* bstrwrap_tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = bstrwrap_tests.h; path = test/bstrwrap_tests.h; sourceTree = "<group>"; };
 		E73B5755216071900080D658 /* bstrwrap_tests.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = bstrwrap_tests.c; path = test/bstrwrap_tests.c; sourceTree = "<group>"; };
 		E73B57572161CA680080D658 /* ieee754.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ieee754.h; path = src/ieee754.h; sourceTree = "<group>"; };
@@ -45,8 +47,8 @@
 		E776E07C214ADF7F00E67947 /* QCBOR */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR; sourceTree = BUILT_PRODUCTS_DIR; };
 		E776E08C214AE07400E67947 /* qcbor_encode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_encode.c; path = src/qcbor_encode.c; sourceTree = "<group>"; tabWidth = 3; };
 		E776E08D214AE07500E67947 /* UsefulBuf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = UsefulBuf.c; path = src/UsefulBuf.c; sourceTree = "<group>"; };
-		E776E08E214AE07500E67947 /* qcbor_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_decode.c; path = src/qcbor_decode.c; sourceTree = "<group>"; };
-		E776E093214AE08B00E67947 /* qcbor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor.h; path = inc/qcbor.h; sourceTree = "<group>"; };
+		E776E08E214AE07500E67947 /* qcbor_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_decode.c; path = src/qcbor_decode.c; sourceTree = "<group>"; tabWidth = 3; };
+		E776E093214AE08B00E67947 /* qcbor.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor.h; path = inc/qcbor.h; sourceTree = "<group>"; tabWidth = 3; };
 		E776E094214AE09700E67947 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/UsefulBuf.h; sourceTree = "<group>"; tabWidth = 3; };
 		E776E096214AE0C700E67947 /* cmd_line_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_line_main.c; sourceTree = "<group>"; };
 		E776E09B214AEEEA00E67947 /* basic_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = basic_test.h; path = test/basic_test.h; sourceTree = "<group>"; };
@@ -88,6 +90,7 @@
 		E776E08B214AE06600E67947 /* src */ = {
 			isa = PBXGroup;
 			children = (
+				0F60E27A2164705400483952 /* qcbor_decode_malloc.c */,
 				E73B57582161CA690080D658 /* ieee754.c */,
 				E73B57572161CA680080D658 /* ieee754.h */,
 				E776E08E214AE07500E67947 /* qcbor_decode.c */,
@@ -180,6 +183,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				E776E08F214AE07500E67947 /* qcbor_encode.c in Sources */,
+				0F60E27B2164705400483952 /* qcbor_decode_malloc.c in Sources */,
 				E73B57592161CA690080D658 /* ieee754.c in Sources */,
 				E73B575F2161CA7C0080D658 /* half_to_double_from_rfc7049.c in Sources */,
 				E73B57652161F8F80080D658 /* run_tests.c in Sources */,
diff --git a/inc/UsefulBuf.h b/inc/UsefulBuf.h
index 39c518f..aeb20eb 100644
--- a/inc/UsefulBuf.h
+++ b/inc/UsefulBuf.h
@@ -372,6 +372,9 @@
  */
 UsefulBufC UsefulBuf_Copy(UsefulBuf Dest, const UsefulBufC Src);
 
+UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src);
+
+
 
 /**
  @brief Set all bytes in a UsefulBuf to a value, for example 0
@@ -633,6 +636,12 @@
  */
 void UsefulOutBuf_Init(UsefulOutBuf *me, UsefulBuf Storage);
 
+static inline void UsefulOutBuf_Realloc(UsefulOutBuf *me, UsefulBuf Storage)
+{
+   me->UB = Storage;
+}
+
+
 
 
 /** Convenience marco to make a UsefulOutBuf on the stack and
diff --git a/inc/qcbor.h b/inc/qcbor.h
index eec84c1..4ea4079 100644
--- a/inc/qcbor.h
+++ b/inc/qcbor.h
@@ -180,8 +180,15 @@
    UsefulInputBuf InBuf;
    
    uint8_t        uDecodeMode;
+   uint8_t        bStringAllocateAll; // TODO: fully implement this
    
    QCBORDecodeNesting nesting;
+   
+   // This is NULL or points to a QCBORStringAllocator. It is void
+   // here because _QCBORDecodeContext is defined early n the
+   // private part of this file and QCBORStringAllocat is defined
+   // later in the public part of this file.
+   void *pStringAllocator;
 };
 
 
@@ -581,6 +588,17 @@
 /** Closing something different than is open */
 #define QCBOR_ERR_CLOSE_MISMATCH          17
 
+/** Unable to decode an indefinitely length string because no string allocator was configured */
+#define QCBOR_ERR_NO_STRING_ALLOCATOR     18
+
+/** One of the segments in an indefinite length string is of the wrong type */
+#define QCBOR_ERR_INDEFINITE_STRING_SEG   19
+
+/** Error allocating space for indefinite length string segment */
+#define QCBOR_ERR_STRING_ALLOC            20
+
+/** The a break occurred outside an indefinite length item */
+#define QCBOR_ERR_BAD_BREAK               21
 
 
 /** See QCBORDecode_Init() */
@@ -630,9 +648,10 @@
 /** Type for the simple value undef; nothing more; nothing in val union. */
 #define QCBOR_TYPE_UNDEF         23
 
+#define QCBOR_TYPE_BREAK         31 // Used internally; never returned
 
 #define QCBOR_TYPE_OPTTAG     254 // Used internally; never returned
-#define QCBOR_TYPE_BREAK      255 // Used internally; never returned
+//#define QCBOR_TYPE_BREAK      255 // Used internally; never returned
 
 
 
@@ -651,6 +670,7 @@
    uint8_t  uDataType;     /** Tells what element of the val union to use. One of QCBOR_TYPE_XXXX */
    uint8_t  uNestingLevel; /** How deep the nesting from arrays and maps are. 0 is the top level with no arrays or maps entered */
    uint8_t  uLabelType;    /** Tells what element of the label union to use */
+   uint8_t  uAllocated;    /** 1 if allocated with string allocator, 0 if not. See xxx TODO: */
    
    union {
       int64_t     int64;      /** The value for uDataType QCBOR_TYPE_INT64 */
@@ -682,6 +702,19 @@
 } QCBORItem;
 
 
+/*
+ Optional to set up an allocator for bstr and tstr types.
+ Required to process indefinite length bstr and tstr types.
+ (indefinite length maps can be processed without this).
+ */
+typedef struct {
+   void    *pAllocaterContext;
+   UsefulBuf (*fAllocate)(void *pAllocaterContext, void *pOldMem, size_t uNewSize);
+   void   (*fFree)(void *pAllocaterContext, void *pMem);
+   void   (*fDestructor)(void *pAllocaterContext);
+} QCBORStringAllocator;
+
+
 /** See the descriptions for CBOR_SIMPLEV_FALSE, CBOR_TAG_DATE_EPOCH... for
     the meaning of the individual tags.  The values here are bit flags
     associated with each tag.  These flags are set in uTagsBits in QCBORItem */
@@ -1609,6 +1642,49 @@
 
 
 /**
+ Set up a memory pool for indefinite length strings
+ 
+ @param[in] pCtx The decode context to initialize.
+ @param[in] MemPool The pointer and length of the memory pool
+ @param[in] bAllStrings true means to put even definite length strings in the pool
+ 
+ Indefinite length strings (text and byte) cannot be decoded unless there is
+ either a memory pool set up or a string allocator configured.
+ 
+ The MemPool must be sized large enough. For a 64-bit CPU it must be sized
+ at 64 bytes plus space to hold all the indefinite length strings. For a 32-bit CPU
+ the size is 32 bytes plus space for the strings. There is no overhead per
+ string. This includes indefinite length strings that are serve as labels
+ for map items.
+ 
+ If bAllStrings is set then the size will be 64 or 32 bytes plus the space to
+ hold **all** strings, definite and indefinite length, value or label.
+ The advantage of this
+ is that after the parse is complete, the original memory holding the
+ encoded CBOR does not need to remain valid.
+ 
+ The memory from MemPool will be where all the pointers in returned
+ QCBORItems will point.
+ 
+ 
+ 
+ */
+void QCBORDecode_SetMemPool(QCBORDecodeContext *pCtx, UsefulBuf MemPool, bool bAllStrings);
+
+
+int QCBORDecode_SetMemPool_Init(QCBORDecodeContext *pCtx, UsefulBufC EncodedCBOR, int8_t nMode, UsefulBuf MemPool);
+
+
+/**
+ 
+ */
+void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator);
+
+QCBORStringAllocator *QCBORDecode_GetAllocator(QCBORDecodeContext *pCtx);
+
+
+
+/**
  Gets the next item (integer, byte string, array...) in pre order traversal of CBOR tree
  
  @param[in]  pCtx          The context to initialize
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index 323e173..3aa6406 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -93,6 +93,18 @@
    return((UsefulBufC){Dest.ptr, Src.len});
 }
 
+
+UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src)
+{
+    if(Src.len > Dest.len - uOffset) {
+        return NULLUsefulBufC;
+    }
+    
+    memcpy(Dest.ptr + uOffset, Src.ptr, Src.len);
+    
+    return((UsefulBufC){Dest.ptr, Src.len});
+}
+
 /*
    Public function -- see UsefulBuf.h
  */
@@ -170,6 +182,7 @@
     
     // The following check fails on ThreadX
 #if 0
+    // TODO: fix this for new way of doing storage
     // Sanity check on the pointer and size to be sure we are not
     // passed a buffer that goes off the end of the address space.
     // Given this test, we know that all unsigned lengths less than
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 7e441cd..eccd469 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -93,15 +93,30 @@
    return pNesting->pCurrent != &(pNesting->pMapsAndArrays[0]);
 }
 
+inline static int DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
+{
+   if(!DecodeNesting_IsNested(pNesting)) {
+      return 0;
+   }
+   
+   return pNesting->pCurrent->uCount == UINT16_MAX;
+}
+
 inline static int DecodeNesting_TypeIsMap(const QCBORDecodeNesting *pNesting)
 {
-   if(!DecodeNesting_IsNested(pNesting))
+   if(!DecodeNesting_IsNested(pNesting)) {
       return 0;
+   }
    
    return CBOR_MAJOR_TYPE_MAP == pNesting->pCurrent->uMajorType;
 }
 
-inline static void DecodeNesting_Decrement(QCBORDecodeNesting *pNesting, uint8_t uDataType)
+inline static void DecodeNesting_Ascend(QCBORDecodeNesting *pNesting)
+{
+   pNesting->pCurrent--;
+}
+
+inline static void DecodeNesting_Decrement(QCBORDecodeNesting *pNesting)
 {
    if(!DecodeNesting_IsNested(pNesting)) {
       return;  // at top level where there is no tracking
@@ -109,7 +124,7 @@
    
    // Decrement
    pNesting->pCurrent->uCount--;
-   
+
    // Pop up nesting levels if the counts at the levels is zero
    while(0 == pNesting->pCurrent->uCount && DecodeNesting_IsNested(pNesting)) {
       pNesting->pCurrent--;
@@ -167,6 +182,15 @@
 
 
 /*
+ Public function, see header file
+ */
+void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator)
+{
+    pCtx->pStringAllocator = (void *)pAllocator;
+}
+
+
+/*
  This decodes the fundamental part of a CBOR data item, the type and number
  
  This is the Counterpart to InsertEncodedTypeAndNumber().
@@ -175,6 +199,11 @@
  also results in the conversion for floats in addition to that for
  lengths, tags and integer values.
  
+ This returns:
+   pnMajorType -- the major type for the item
+   puNumber -- the "number" which is used a the value for integers, tags and floats and length for strings and arrays
+   puAdditionalInfo -- Pass this along to know what kind of float or if length is indefinite
+ 
  */
 inline static int DecodeTypeAndNumber(UsefulInputBuf *pUInBuf, int *pnMajorType, uint64_t *puNumber, uint8_t *puAdditionalInfo)
 {
@@ -188,8 +217,7 @@
    const uint8_t uAdditionalInfo = InitialByte & 0x1f;
    
    // Get the integer that follows the major type. Do not know if this is a length, value, float or tag at this point
-   // Also convert from network byte order. Call ntohxx on simple variables in case they are macros that
-   // reference their argument multiple times.
+   // Also convert from network byte order.
    uint64_t uTmpValue;
    switch(uAdditionalInfo) {
          
@@ -212,11 +240,14 @@
       case ADDINFO_RESERVED1: // reserved by CBOR spec
       case ADDINFO_RESERVED2: // reserved by CBOR spec
       case ADDINFO_RESERVED3: // reserved by CBOR spec
-      case LEN_IS_INDEFINITE: // indefinite types not supported (yet)
          nReturn = QCBOR_ERR_UNSUPPORTED;
          goto Done;
+
+      case LEN_IS_INDEFINITE:
+         // Fall through to see what happens: TODO: check this.
          
       default:
+         // This is when the "number" is in the additional info
          uTmpValue = uAdditionalInfo;
          break;
    }
@@ -300,6 +331,10 @@
 #error QCBOR_TYPE_UNDEF macro value wrong
 #endif
 
+#if QCBOR_TYPE_BREAK != CBOR_SIMPLE_BREAK
+#error QCBOR_TYPE_BREAK macro value wrong
+#endif
+
 #if QCBOR_TYPE_DOUBLE != DOUBLE_PREC_FLOAT
 #error QCBOR_TYPE_DOUBLE macro value wrong
 #endif
@@ -324,7 +359,6 @@
       case ADDINFO_RESERVED1:  // 28
       case ADDINFO_RESERVED2:  // 29
       case ADDINFO_RESERVED3:  // 30
-      case CBOR_SIMPLE_BREAK:  // 31
          nReturn = QCBOR_ERR_UNSUPPORTED;
          break;
            
@@ -343,6 +377,7 @@
       case CBOR_SIMPLEV_TRUE:  // 21
       case CBOR_SIMPLEV_NULL:  // 22
       case CBOR_SIMPLEV_UNDEF: // 23
+      case CBOR_SIMPLE_BREAK:  // 31
          break; // nothing to do
          
       case CBOR_SIMPLEV_ONEBYTE: // 24
@@ -370,7 +405,7 @@
 /*
  Decode text and byte strings
  */
-inline static int DecodeBytes(int nMajorType, uint64_t uNumber, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
+inline static int DecodeBytesOld(int nMajorType, uint64_t uNumber, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
 {
    const void *pBytes = UsefulInputBuf_GetBytes(pUInBuf, uNumber);
    
@@ -385,6 +420,30 @@
    return nReturn;
 }
 
+inline static int DecodeBytes(QCBORStringAllocator *pAlloc, int nMajorType, uint64_t uNumber, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
+{
+    UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(pUInBuf, uNumber);
+   
+   int nReturn = QCBOR_ERR_HIT_END;
+   
+   if(!UsefulBuf_IsNULLC(Bytes)) {
+      if(pAlloc) {
+         UsefulBuf NewMem = pAlloc->fAllocate(pAlloc->pAllocaterContext, NULL, uNumber);
+         if(UsefulBuf_IsNULL(NewMem)) {
+            pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes);
+         } else {
+            // TODO: failure
+         }
+      } else {
+         pDecodedItem->val.string = Bytes;
+      }
+      pDecodedItem->uDataType  = (nMajorType == CBOR_MAJOR_TYPE_BYTE_STRING) ? QCBOR_TYPE_BYTE_STRING : QCBOR_TYPE_TEXT_STRING;
+      nReturn = QCBOR_SUCCESS;
+   }
+   
+   return nReturn;
+}
+
 
 /*
  Mostly just assign the right data type for the date string.
@@ -453,21 +512,203 @@
 }
 
 
+
+
+// Make sure the constants align as this is assumed by the GetAnItem() implementation
+#if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY
+#error QCBOR_TYPE_ARRAY value not lined up with major type
+#endif
+#if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP
+#error QCBOR_TYPE_MAP value not lined up with major type
+#endif
+
 /*
- Decode the optional tagging that preceeds the real data value. There could be lots of them.
+ This gets a single data item and decodes it including preceding optional tagging. This does not
+ deal with arrays and maps and nesting except to decode the data item introducing them. Arrays and
+ maps are handled at the next level up in GetNext().
+ 
+ Errors detected here include: an array that is too long to decode, hit end of buffer unexpectedly,
+    a few forms of invalid encoded CBOR
  */
-static int GetAnItem(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, int bCalledFromDecodeOptional);
+
+static int GetNext_Item(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, QCBORStringAllocator *pAlloc)
+{
+   int nReturn;
+   
+   // Get the major type and the number. Number could be length of more bytes or the value depending on the major type
+   // nAdditionalInfo is an encoding of the length of the uNumber and is needed to decode floats and doubles
+   int      uMajorType;
+   uint64_t uNumber;
+   uint8_t  uAdditionalInfo;
+   
+   nReturn = DecodeTypeAndNumber(pUInBuf, &uMajorType, &uNumber, &uAdditionalInfo);
+   
+   // Error out here if we got into trouble on the type and number.
+   // The code after this will not work if the type and number is not good.
+   if(nReturn)
+      goto Done;
+   
+   pDecodedItem->uTagBits = 0;
+   pDecodedItem->uTag     = 0;
+   pDecodedItem->uAllocated = 0;
+   
+   // At this point the major type and the value are valid. We've got the type and the number that
+   // starts every CBOR data item.
+   switch (uMajorType) {
+      case CBOR_MAJOR_TYPE_POSITIVE_INT: // Major type 0
+      case CBOR_MAJOR_TYPE_NEGATIVE_INT: // Major type 1
+         nReturn = DecodeInteger(uMajorType, uNumber, pDecodedItem);
+         break;
+         
+      case CBOR_MAJOR_TYPE_BYTE_STRING: // Major type 2
+      case CBOR_MAJOR_TYPE_TEXT_STRING: // Major type 3
+         if(uAdditionalInfo == LEN_IS_INDEFINITE) {
+            pDecodedItem->uDataType  = (uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING) ? QCBOR_TYPE_BYTE_STRING : QCBOR_TYPE_TEXT_STRING;
+            pDecodedItem->val.string = (UsefulBufC){NULL, SIZE_MAX}; // TODO: SIZE_MAX this is OK, right?
+         } else {
+            nReturn = DecodeBytes(pAlloc, uMajorType, uNumber, pUInBuf, pDecodedItem);
+         }
+         break;
+         
+      case CBOR_MAJOR_TYPE_ARRAY: // Major type 4
+      case CBOR_MAJOR_TYPE_MAP:   // Major type 5
+         // Record the number of items in the array or map
+         if(uNumber > QCBOR_MAX_ITEMS_IN_ARRAY) {
+            nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+            goto Done;
+         }
+         if(uAdditionalInfo == LEN_IS_INDEFINITE) {
+            pDecodedItem->val.uCount = UINT16_MAX; // Indicate indefinite length; TODO: a better way?
+         } else {
+            pDecodedItem->val.uCount = (uint16_t)uNumber; // type conversion OK because of check above
+         }
+         pDecodedItem->uDataType  = uMajorType; // C preproc #if above makes sure constants align
+         break;
+         
+      case CBOR_MAJOR_TYPE_OPTIONAL: // Major type 6, optional prepended tags
+         pDecodedItem->uTag      = uNumber;
+         pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
+         break;
+         
+      case CBOR_MAJOR_TYPE_SIMPLE: // Major type 7, float, double, true, false, null...
+         nReturn = DecodeSimple(uAdditionalInfo, uNumber, pDecodedItem);
+         break;
+         
+      default: // Should never happen because DecodeTypeAndNumber() should never return > 7
+         nReturn = QCBOR_ERR_UNSUPPORTED;
+         break;
+   }
+   
+Done:
+   return nReturn;
+}
+
+
+
+/*
+ This layer deals with indefinite length strings
+ 
+ */
+static int GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+   int nReturn;
+   QCBORStringAllocator *pAlloc =  (QCBORStringAllocator *)me->pStringAllocator;
+   
+   nReturn = GetNext_Item(&(me->InBuf), pDecodedItem, me->bStringAllocateAll ? pAlloc: NULL);
+   if(nReturn) {
+      goto Done;
+   }
+   
+   // To reduce code size by removing support for indefinite length strings, the
+   // code in this function from here down can be eliminated. Run tests to be sure
+   // all is OK if you remove this.
+   if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+      nReturn = QCBOR_ERR_BAD_BREAK;
+      goto Done;
+   }
+   
+   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING && pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+      goto Done; // no need to do any work here on non-string types
+   }
+   
+   if(pDecodedItem->val.string.len != SIZE_MAX) { // TODO: is this right? Is there a better way to mark this?
+      goto Done; // length is not indefinite, so no work to do
+   }
+   
+   if(pAlloc == NULL) {
+      // can't do indefinite length strings without an allocator
+      nReturn = QCBOR_ERR_NO_STRING_ALLOCATOR;
+      goto Done;
+   }
+   
+   // There is an indefinite length string to work on...
+   const uint8_t uStringType = pDecodedItem->uDataType;
+   
+   // Loop getting segments of indefinite string
+   UsefulBufC FullString = NULLUsefulBufC;
+   for(;;) {
+      // Get item for next segment
+      QCBORItem Item;
+      nReturn = GetNext_Item(&(me->InBuf), &Item, NULL); // Never alloc segments of indefinite length strings
+      if(nReturn) {
+         // Error getting the next segment
+         break;
+      }
+      
+      // See if it is marker at end of indefinite length string
+      if(Item.uDataType == QCBOR_TYPE_BREAK) {
+         // SUCCESS!!!
+         pDecodedItem->val.string = FullString;
+         pDecodedItem->uAllocated = 1;
+         break;
+      }
+      
+      // Match data type of segment to type at beginning
+      if(Item.uDataType != uStringType) {
+         nReturn = QCBOR_ERR_INDEFINITE_STRING_SEG;
+         break;
+      }
+      
+      // Expand the buffer so it can fit
+      UsefulBuf NewMem = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, (void *)FullString.ptr, FullString.len + Item.val.string.len);
+      if(UsefulBuf_IsNULL(NewMem)) {
+         nReturn = QCBOR_ERR_STRING_ALLOC;
+         break;
+      }
+      
+      // Copy data to the end of it.
+      FullString = UsefulBuf_CopyOffset(NewMem, FullString.len, Item.val.string);
+   }
+   
+Done:
+   if(nReturn && FullString.ptr) {
+      // Getting item failed, clean up the allocated memory
+      (pAlloc->fFree)(pAlloc->pAllocaterContext, (void *)FullString.ptr); // TODO unconst construct
+   }
+   
+   return nReturn;
+}
+
 
 /*
  Returns an error if there was something wrong with the optional item or it couldn't
  be handled.
  */
-static int DecodeOptional(UsefulInputBuf *pUInBuf, uint64_t uInputTag, QCBORItem *pDecodedItem)
+static int GetNext_TaggedItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
 {
-   int      nReturn = QCBOR_SUCCESS;
+   int nReturn;
+   
+   nReturn = GetNext_FullItem(me, pDecodedItem);
+   if(nReturn) {
+      goto Done;
+   }
+   
+   if(pDecodedItem->uDataType != QCBOR_TYPE_OPTTAG) {
+      goto Done;
+   }
    
    uint64_t uTagFlags = 0; // accumulate the tags in the form of flags
-   uint64_t uTagToProcess = uInputTag; // First process tag passed in
+   uint64_t uTagToProcess = pDecodedItem->uTag; // First process tag passed in
    
    QCBORItem Item;
    
@@ -478,11 +719,11 @@
          uTagFlags |= QCBOR_TAGFLAG_CBOR_MAGIC;
       }
       /* This code ignores the all but the first tag of value
-         greater than 63. Ignoring tags that are not understoof
-         is allowed by the standard. Multiple tags are 
-         presumably rare. */
+       greater than 63. Ignoring tags that are not understoof
+       is allowed by the standard. Multiple tags are
+       presumably rare. */
       
-      nReturn = GetAnItem(pUInBuf, &Item, 1);
+      nReturn = GetNext_FullItem(me, &Item);
       if(nReturn) {
          // Bail out of the whole item fetch on any sort of error here
          goto Done;
@@ -494,22 +735,22 @@
       
       uTagToProcess = Item.uTag;
    } while (1);
-
+   
    
    /*
-     CBOR allows multiple tags on a data item. It also defines
-     a number of standard tag values, most of which are 
-     less than 64.  This code can deal with multiple tag
-     values that are less than 64 and the last tag of multiple
-     if the value is more than 64. Or said another way
-     if there is one tag with a value >64 this code works. 
+    CBOR allows multiple tags on a data item. It also defines
+    a number of standard tag values, most of which are
+    less than 64.  This code can deal with multiple tag
+    values that are less than 64 and the last tag of multiple
+    if the value is more than 64. Or said another way
+    if there is one tag with a value >64 this code works.
     
-     The assumption is that multiple tag values > 64 are rare.
+    The assumption is that multiple tag values > 64 are rare.
     
-     At this point in this code. uTagFlags has all the flags
-     < 64 and uTagToProcess has the last tag.
+    At this point in this code. uTagFlags has all the flags
+    < 64 and uTagToProcess has the last tag.
     
-     Does this deal with multiple tags on an item we process?
+    Does this deal with multiple tags on an item we process?
     */
    
    Item.uTagBits = uTagFlags;
@@ -538,93 +779,6 @@
          // Encountering some mixed up CBOR like something that
          // is tagged as both a string and integer date.
          nReturn = QCBOR_ERR_BAD_OPT_TAG ;
-      }
-
-Done:
-   return nReturn;
-}
-
-
-
-// Make sure the constants align as this is assumed by the GetAnItem() implementation
-#if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY
-#error QCBOR_TYPE_ARRAY value not lined up with major type
-#endif
-#if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP
-#error QCBOR_TYPE_MAP value not lined up with major type
-#endif
-
-/*
- This gets a single data item and decodes it including preceding optional tagging. This does not
- deal with arrays and maps and nesting except to decode the data item introducing them. Arrays and
- maps are handled at the next level up in GetNext().
- 
- Errors detected here include: an array that is too long to decode, hit end of buffer unexpectedly,
-    a few forms of invalid encoded CBOR
- */
-
-static int GetAnItem(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, int bCalledFromDecodeOptional)
-{
-   int nReturn;
-   
-   // Get the major type and the number. Number could be length of more bytes or the value depending on the major type
-   // nAdditionalInfo is an encoding of the length of the uNumber and is needed to decode floats and doubles
-   int      uMajorType;
-   uint64_t uNumber;
-   uint8_t  uAdditionalInfo;
-   
-   nReturn = DecodeTypeAndNumber(pUInBuf, &uMajorType, &uNumber, &uAdditionalInfo);
-   
-   // Error out here if we got into trouble on the type and number.
-   // The code after this will not work if the type and number is not good.
-   if(nReturn)
-      goto Done;
-   
-   pDecodedItem->uTagBits = 0;
-   pDecodedItem->uTag     = 0;
-   
-   // At this point the major type and the value are valid. We've got the type and the number that
-   // starts every CBOR data item.
-   switch (uMajorType) {
-      case CBOR_MAJOR_TYPE_POSITIVE_INT: // Major type 0
-      case CBOR_MAJOR_TYPE_NEGATIVE_INT: // Major type 1
-         nReturn = DecodeInteger(uMajorType, uNumber, pDecodedItem);
-         break;
-         
-      case CBOR_MAJOR_TYPE_BYTE_STRING: // Major type 2
-      case CBOR_MAJOR_TYPE_TEXT_STRING: // Major type 3
-         nReturn = DecodeBytes(uMajorType, uNumber, pUInBuf, pDecodedItem);
-         break;
-         
-      case CBOR_MAJOR_TYPE_ARRAY: // Major type 4
-      case CBOR_MAJOR_TYPE_MAP:   // Major type 5
-         // Record the number of items in the array or map
-         if(uNumber > QCBOR_MAX_ITEMS_IN_ARRAY) {
-            nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
-            goto Done;
-         }
-         pDecodedItem->val.uCount = uNumber; // type conversion OK because of check above
-         pDecodedItem->uDataType  = uMajorType; // C preproc #if above makes sure constants align
-         break;
-         
-      case CBOR_MAJOR_TYPE_OPTIONAL: // Major type 6, optional prepended tags
-         pDecodedItem->uTag      = uNumber;
-         pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
-         if(!bCalledFromDecodeOptional) {
-            // There can be a more than one optional tag in front of an actual data item
-            // they are all handled by looping in DecodeOptional which calls back here
-            // this test avoids infinite recursion.
-            nReturn = DecodeOptional(pUInBuf, uNumber, pDecodedItem);
-         }
-         break;
-         
-      case CBOR_MAJOR_TYPE_SIMPLE: // Major type 7, float, double, true, false, null...
-         nReturn = DecodeSimple(uAdditionalInfo, uNumber, pDecodedItem);
-         break;
-         
-      default: // Should never happen because DecodeTypeAndNumber() should never return > 7
-         nReturn = QCBOR_ERR_UNSUPPORTED;
-         break;
    }
    
 Done:
@@ -633,18 +787,11 @@
 
 
 /*
- Public function, see header qcbor.h file
+ This layer takes care of map entries. It combines the label and data items into one item.
  */
-int QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+static inline int GetNext_MapEntry(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
 {
-   int nReturn;
-   
-   if(!UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
-      nReturn = QCBOR_ERR_HIT_END;
-      goto Done;
-   }
-   
-   nReturn = GetAnItem(&(me->InBuf), pDecodedItem, 0);
+   int nReturn = GetNext_TaggedItem(me, pDecodedItem);
    if(nReturn)
       goto Done;
    
@@ -654,7 +801,7 @@
       
       // Get the next item which will be the real data; Item will be the label
       QCBORItem LabelItem = *pDecodedItem;
-      nReturn = GetAnItem(&(me->InBuf), pDecodedItem, 0);
+      nReturn = GetNext_TaggedItem(me, pDecodedItem); // TODO: test indefinite length string labels
       if(nReturn)
          goto Done;
       
@@ -666,7 +813,7 @@
          // It's not a string and we only want strings, probably for easy translation to JSON
          nReturn = QCBOR_ERR_MAP_LABEL_TYPE;
          goto Done;
-     } else if(LabelItem.uDataType == QCBOR_TYPE_INT64) {
+      } else if(LabelItem.uDataType == QCBOR_TYPE_INT64) {
          pDecodedItem->label.int64 = LabelItem.val.int64;
          pDecodedItem->uLabelType = QCBOR_TYPE_INT64;
       } else if(LabelItem.uDataType == QCBOR_TYPE_UINT64) {
@@ -683,6 +830,58 @@
       }
    }
    
+Done:
+   return nReturn;
+}
+
+
+/*
+ Public function, see header qcbor.h file
+ */
+/*
+ Decoding items is done in layers:
+ - This top layer takes care of tracking for decsending into and
+   ascending out of maps and arrays  GetNext()
+ - The next layer takes care of map entries that are made up
+   of a label and a data item. They are returned as one. GetNextMap() GetNext_MapEntry()
+ - The next layer takes care of tagging and tagged types  GetNext_TaggedItem()
+ - The next layer takes care of indefinite length strings GetFullItem()  GetNext_FullItem()
+ - The next layer does the main decoding of the non-compound GetAnItem()  GetNext_Item()
+   items, all the different types of them
+ 
+ */
+int QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+   int nReturn;
+   
+   if(!UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
+      nReturn = QCBOR_ERR_HIT_END;
+      goto Done;
+   }
+   
+   nReturn = GetNext_MapEntry(me, pDecodedItem);
+   if(nReturn) {
+      goto Done;
+   }
+   
+   // Handle any breaks that are closing out arrays or maps
+   while(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+      if(!DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
+         nReturn = QCBOR_ERR_BAD_BREAK;
+         goto Done;
+      }
+      
+      // Close out and ascend
+      DecodeNesting_Ascend(&(me->nesting));
+      
+      nReturn = GetNext_MapEntry(me, pDecodedItem);
+      if(nReturn) {
+         goto Done;
+      }
+   }
+   
+   // Now have the next item that is not a break. The one that is going to be returned
+
    // Record the nesting level for this data item
    pDecodedItem->uNestingLevel = DecodeNesting_GetLevel(&(me->nesting));
    
@@ -691,8 +890,11 @@
       nReturn = DecodeNesting_Descend(&(me->nesting), pDecodedItem->uDataType, pDecodedItem->val.uCount);
    } else {
       // Track number of items in maps and arrays and ascend nesting if all are consumed
-      // Note that an empty array or map is like a integer or string in effect here
-      DecodeNesting_Decrement(&(me->nesting), pDecodedItem->uDataType);
+      // Note that an empty array or map is like an integer or string in effect here
+      // No counting is done for indefinite length arrays
+      if(!DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
+         DecodeNesting_Decrement(&(me->nesting));
+      }
    }
    
 Done:
@@ -705,19 +907,59 @@
  */
 int QCBORDecode_Finish(QCBORDecodeContext *me)
 {
-   return UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) ? QCBOR_ERR_EXTRA_BYTES : QCBOR_SUCCESS;
+   int nReturn = QCBOR_SUCCESS;
+   
+   // Consume any breaks ending indefinite length arrays or maps here
+   // Definite length arrays and maps get closed off above when they
+   // the last item in them is consumed; they are not handled here.
+   while(DecodeNesting_GetLevel(&(me->nesting))) {
+      QCBORItem Item;
+      GetNext_Item(&(me->InBuf), &Item, false);
+      if(Item.uDataType != QCBOR_TYPE_BREAK) {
+         nReturn = QCBOR_ERR_EXTRA_BYTES;
+         break;
+      }
+      DecodeNesting_Ascend(&(me->nesting));
+   }
+   
+   // Call the desctructor for the string allocator if there is one
+   if(me->pStringAllocator) {
+      QCBORStringAllocator *pAllocator = (QCBORStringAllocator *)me->pStringAllocator;
+      if(pAllocator->fDestructor) {
+         (pAllocator->fDestructor)(pAllocator->pAllocaterContext);
+      }
+   }
+   
+   if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
+      nReturn = QCBOR_ERR_EXTRA_BYTES;
+   }
+   
+   return nReturn;
 }
 
 
 
+/*
+ 
+ Use the 64-bit map. 48 8-bit tags built in, 1 16 bit tag, 15 64-bit tags can be assigned as of interest
+ 
+ There is a tag map.
+ 
+ TODO: how does tinyCBOR do it?
+ 
+ 
+ 
+ 
+ 
+ */
+
+
 /* 
  
 Decoder errors handled in this file
  
  - Hit end of input before it was expected while decoding type and number QCBOR_ERR_HIT_END
- 
- - indefinite length, currently not supported QCBOR_ERR_UNSUPPORTED
- 
+  
  - negative integer that is too large for C QCBOR_ERR_INT_OVERFLOW
  
  - Hit end of input while decoding a text or byte string QCBOR_ERR_HIT_END
@@ -738,3 +980,70 @@
  
  */
 
+
+
+typedef struct {
+   QCBORStringAllocator  StringAllocator;
+   uint8_t              *pStart;
+   uint8_t              *pEnd;
+   uint8_t              *pFree;
+} MemPool;
+
+
+/*
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+static UsefulBuf MemPool_Alloc(void *ctx, void *pMem, size_t uNewSize)
+{
+   MemPool *me = (MemPool *)ctx;
+   void *pReturn = NULL;
+   
+   if(pMem) {
+      // Realloc case
+      // TODO: review this pointer math
+      if((uint8_t *)pMem + uNewSize <= me->pEnd && (uint8_t *)pMem > me->pStart) {
+         me->pFree = pMem + uNewSize;
+         pReturn = pMem;
+      }
+   } else {
+      // New chunk case
+      if(me->pFree + uNewSize <= me->pEnd) {
+         pReturn = me->pFree;
+         me->pFree += uNewSize;
+      }
+   }
+   
+   return (UsefulBuf){pReturn, uNewSize};
+}
+
+
+static void MemPool_Free(void *ctx, void *pOldMem)
+{
+   MemPool *me = (MemPool *)ctx;
+   me->pFree = pOldMem;
+}
+
+
+void QCBORDecode_SetMemPool(QCBORDecodeContext *me, UsefulBuf Pool, bool bAllStrings)
+{
+   if(Pool.len < sizeof(MemPool)+1) {
+      return; // Failure will happen when first string needs to be allocated
+   }
+   
+   MemPool *pMP = (MemPool *)Pool.ptr;
+   
+   pMP->StringAllocator.fAllocate   = MemPool_Alloc;
+   pMP->StringAllocator.fFree       = MemPool_Free;
+   pMP->StringAllocator.fDestructor = NULL;
+   
+   pMP->pStart = Pool.ptr + sizeof(MemPool);
+   pMP->pFree  = pMP->pStart;
+   pMP->pEnd   = Pool.ptr + Pool.len;
+   pMP->StringAllocator.pAllocaterContext = pMP;
+   
+   me->pStringAllocator = pMP;
+   me->bStringAllocateAll = bAllStrings;
+}
+
+
+
diff --git a/src/qcbor_decode_malloc.c b/src/qcbor_decode_malloc.c
new file mode 100644
index 0000000..4e405e0
--- /dev/null
+++ b/src/qcbor_decode_malloc.c
@@ -0,0 +1,65 @@
+//
+//  qcbor_decode_malloc.c
+//  QCBOR
+//
+//  Created by Laurence Lundblade on 10/1/18.
+//  Copyright © 2018 Laurence Lundblade. All rights reserved.
+//
+
+#include "qcbor.h"
+#include <stdlib.h> // for realloc and free
+
+static UsefulBuf MemMallocAlloc(void *ctx, void *pOldMem, size_t uNewSize)
+{
+    void *pNewMem = realloc(pOldMem, uNewSize);
+    return (UsefulBuf){pNewMem, uNewSize};
+}
+
+static void MemMallocFree(void *ctx, void *old)
+{
+    free(old);
+}
+
+static void MemMallocDestructor(void *ctx)
+{
+    free(ctx);
+}
+
+
+QCBORStringAllocator *QCBORDecode_MakeMallocStringAllocator()
+{
+    QCBORStringAllocator *pAllocaterContext = malloc(sizeof(QCBORStringAllocator));
+    if(pAllocaterContext) {
+        pAllocaterContext->fAllocate   = MemMallocAlloc;
+        pAllocaterContext->fFree       = MemMallocFree;
+        pAllocaterContext->fDestructor = MemMallocDestructor;
+    }
+    
+    return pAllocaterContext;
+}
+
+
+/*
+void QCBORDecodeMalloc_Init(QCBORDecodeContext *me, UsefulBufC EncodedCBOR, int8_t nDecodeMode)
+{
+    QCBORDecode_Init(me, EncodedCBOR, nDecodeMode);
+    
+    QCBORStringAllocator *pAllocaterContext = malloc(sizeof(QCBORStringAllocator));
+    
+    pAllocaterContext->fAllocate = MemMalloc;
+    pAllocaterContext->fFree = MemFree;
+    
+    QCBORDecode_SetUpAllocator(me, pAllocaterContext);
+}
+
+
+int QCBORDecodeMalloc_Finish(QCBORDecodeContext *me)
+{
+    const QCBORStringAllocator *pAllocator = QCBORDecode_GetAllocator(me);
+    
+    free((void *)pAllocator); // TODO: better way to cast away const here
+    return QCBORDecode_Finish(me);
+} */
+
+
+
diff --git a/test/basic_test.c b/test/basic_test.c
index 508c6fd..8241302 100644
--- a/test/basic_test.c
+++ b/test/basic_test.c
@@ -138,3 +138,88 @@
 
 
 
+static const uint8_t pIndefiniteLenString[] = {
+   0x81, // Array of length one
+   0x7f, // text string marked with indefinite length
+      0x65, 0x73, 0x74, 0x72, 0x65, 0x61, // first segment
+      0x64, 0x6d, 0x69, 0x6e, 0x67, // second segment
+   0xff // ending break
+};
+
+static const uint8_t pIndefiniteArray[] = {0x9f, 0x01, 0x82, 0x02, 0x03, 0xff};
+
+//0x9f018202039f0405ffff
+
+int indefinite_length_decode_test() {
+    UsefulBufC IndefLen = UsefulBuf_FromByteArrayLiteral(pIndefiniteArray);
+    
+    
+    // Decode it and see if it is OK
+   UsefulBuf_MakeStackUB(MemPool, 200);
+    QCBORDecodeContext DC;
+    QCBORItem Item;
+    QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+   
+   QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+   
+    QCBORDecode_GetNext(&DC, &Item);
+    if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+        return -1;
+    }
+
+   QCBORDecode_GetNext(&DC, &Item);
+   if(Item.uDataType != QCBOR_TYPE_INT64) {
+      return -1;
+   }
+   
+    QCBORDecode_GetNext(&DC, &Item);
+    if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+        return -1;
+    }
+    
+    QCBORDecode_GetNext(&DC, &Item);
+    if(Item.uDataType != QCBOR_TYPE_INT64) {
+        return -1;
+    }
+    
+    QCBORDecode_GetNext(&DC, &Item);
+    if(Item.uDataType != QCBOR_TYPE_INT64) {
+        return -1;
+    }
+   
+   if(QCBORDecode_Finish(&DC)) {
+      return -2;
+   }
+    
+    return 0;
+}
+
+int indefinite_length_decode_string_test() {
+   UsefulBufC IndefLen = UsefulBuf_FromByteArrayLiteral(pIndefiniteLenString);
+   
+   
+   // Decode it and see if it is OK
+   QCBORDecodeContext DC;
+   QCBORItem Item;
+   UsefulBuf_MakeStackUB(MemPool, 200);
+
+   QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+   
+   QCBORDecode_SetMemPool(&DC,  MemPool, false);
+
+   
+   QCBORDecode_GetNext(&DC, &Item);
+   if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+      return -1;
+   }
+   
+   QCBORDecode_GetNext(&DC, &Item);
+   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return -1;
+   }
+   
+   return 0;
+}
+
+
diff --git a/test/basic_test.h b/test/basic_test.h
index cde44ba..6f3dd66 100644
--- a/test/basic_test.h
+++ b/test/basic_test.h
@@ -31,4 +31,7 @@
 
 int basic_test_one(void);
 
+int indefinite_length_decode_test(void);
+int indefinite_length_decode_string_test(void);
+
 #endif /* basic_test_h */
diff --git a/test/run_tests.c b/test/run_tests.c
index 013b6e5..c3379f3 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -93,6 +93,8 @@
 } test_entry;
 
 test_entry s_tests[] = {
+    TEST_ENTRY(indefinite_length_decode_string_test),
+    TEST_ENTRY(indefinite_length_decode_test),
     TEST_ENTRY(basic_test_one),
     TEST_ENTRY(half_precision_encode_basic),
     TEST_ENTRY(half_precision_decode_basic),
diff --git a/test/run_tests.h b/test/run_tests.h
index 013b6e5..4338dce 100644
--- a/test/run_tests.h
+++ b/test/run_tests.h
@@ -28,126 +28,6 @@
  ==============================================================================*/
 //  Created by Laurence Lundblade on 9/30/18.
 
+typedef int (*outputstring)(const char *szString, void *ctx);
 
-#include "run_tests.h"
-#include "UsefulBuf.h"
-#include <stdbool.h>
-
-#include "half_precision_test.h"
-#include "basic_test.h"
-#include "bstrwrap_tests.h"
-
-// Used to test the test runner
-int fail_test()
-{
-    return -44;
-}
-
-
-/*
- Convert a number up to 999999999 to a string. This is so sprintf doesn't
- have to be linked in so as to minimized dependencies even in test code.
- 
- This function does pointer math. TODO: test this.
- */
-const char *NumToString(int32_t nNum, UsefulBuf StringMem)
-{
-    const uint32_t uMax = 1000000000;
-    
-    UsefulOutBuf OutBuf;
-    UsefulOutBuf_Init(&OutBuf, StringMem);
-    
-    if(nNum < 0) {
-        UsefulOutBuf_AppendByte(&OutBuf, '-');
-        nNum = -nNum;
-    }
-    if(nNum > uMax-1) {
-        return "XXX";
-    }
-    
-    bool bDidSomeOutput = false;
-    for(int n = uMax; n > 0; n/=10) {
-        int x = nNum/n;
-        if(x || bDidSomeOutput){
-            bDidSomeOutput = true;
-            UsefulOutBuf_AppendByte(&OutBuf, '0' + x);
-            nNum -= x * n;
-        }
-    }
-    if(!bDidSomeOutput){
-        UsefulOutBuf_AppendByte(&OutBuf, '0');
-    }
-    UsefulOutBuf_AppendByte(&OutBuf, '\0');
-    
-    return UsefulOutBuf_GetError(&OutBuf) ? "" : StringMem.ptr;
-}
-
-
-
-typedef int (test_fun_t)(void);
-
-#define TEST_ENTRY(test_name)  {#test_name, test_name}
-typedef struct {
-    const char *szTestName;
-    test_fun_t  *test_fun;
-} test_entry;
-
-test_entry s_tests[] = {
-    TEST_ENTRY(basic_test_one),
-    TEST_ENTRY(half_precision_encode_basic),
-    TEST_ENTRY(half_precision_decode_basic),
-    TEST_ENTRY(half_precision_to_float_transitive_test),
-    TEST_ENTRY(double_as_smallest_encode_basic),
-    TEST_ENTRY(half_precision_to_float_vs_rfc_test),
-    TEST_ENTRY(bstrwraptest),
-    TEST_ENTRY(bstr_wrap_error_test),
-    TEST_ENTRY(bstr_wrap_nest_test),
-    TEST_ENTRY(cose_sign1_tbs_test),
-    //TEST_ENTRY(fail_test),
-};
-
-
-int run_tests(outputstring output, void *poutCtx, int *pNumTestsRun)
-{
-    int nTestsFailed = 0;
-    int nTestsRun = 0;
-    UsefulBuf_MakeStackUB(StringStorage, 5);
-    
-    test_entry *t;
-    const test_entry *s_tests_end = s_tests + sizeof(s_tests)/sizeof(test_entry);
-    
-    for(t = s_tests; t < s_tests_end; t++) {
-        int x = (t->test_fun)();
-        nTestsRun++;
-        if(output) {
-            (*output)(t->szTestName, poutCtx);
-        }
-        
-        if(x) {
-            if(output) {
-                (*output)(" FAILED (returned ", poutCtx);
-                (*output)(NumToString(x, StringStorage), poutCtx);
-                (*output)(")\n", poutCtx);
-            }
-            nTestsFailed++;
-        } else {
-            if(output) {
-                (*output)( " PASSED\n", poutCtx);
-            }
-        }
-    }
-    
-    if(pNumTestsRun) {
-        *pNumTestsRun = nTestsRun;
-    }
-    
-    if(output) {
-        (*output)( "SUMMARY: ", poutCtx);
-        (*output)( NumToString(nTestsRun, StringStorage), poutCtx);
-        (*output)( " tests run; ", poutCtx);
-        (*output)( NumToString(nTestsFailed, StringStorage), poutCtx);
-        (*output)( " tests failed\n", poutCtx);
-    }
-    
-    return nTestsFailed;
-}
+int run_tests(outputstring output, void *poutCtx, int *pNumTestsRun);