New #ifndef QCBOR_DISABLE_NON_INTEGER_LABELS

#define QCBOR_DISABLE_NON_INTEGER_LABELS allows label processing for all but integers labels to be disabled to save object code.

There was also a lot of refactoring of the decode in this change for the sake of clarity and object code size reduction.

All totaled, this saves nearly 200 bytes of object code, but that is the growth in object code over the last years from small fixes and features so there's no net gain compared to a years ago. The minimal decoder is still 1550 bytes.


* #define to disable non-integer labels

* Add non-int to CI

* Remove string labels from QCBORItem

* Small corrections

* lots of code tidiness

* optimize out-of-place break error handling

* fix allocate all bug introduced early in PR

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index fc34304..7b8ce5c 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -105,8 +105,10 @@
 {
    const uint8_t uDataType = Item.uDataType;
    return uDataType == QCBOR_TYPE_MAP ||
-          uDataType == QCBOR_TYPE_ARRAY ||
-          uDataType == QCBOR_TYPE_MAP_AS_ARRAY;
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+          uDataType == QCBOR_TYPE_MAP_AS_ARRAY ||
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+          uDataType == QCBOR_TYPE_ARRAY;
 }
 
 static bool
@@ -150,6 +152,7 @@
       if(Item2.uLabelType == QCBOR_TYPE_INT64 && Item1.label.int64 == Item2.label.int64) {
          return true;
       }
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    } else if(Item1.uLabelType == QCBOR_TYPE_TEXT_STRING) {
       if(Item2.uLabelType == QCBOR_TYPE_TEXT_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) {
          return true;
@@ -162,6 +165,7 @@
       if(Item2.uLabelType == QCBOR_TYPE_UINT64 && Item1.label.uint64 == Item2.label.uint64) {
          return true;
       }
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
    }
 
    /* Other label types are never matched */
@@ -373,9 +377,11 @@
    }
 
    uint8_t uItemDataType = pNesting->pCurrentBounded->uLevelType;
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) {
       uItemDataType = QCBOR_TYPE_ARRAY;
    }
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 
    if(uItemDataType != uType) {
       return false;
@@ -845,11 +851,13 @@
 /**
  * @brief Decode integer types, major types 0 and 1.
  *
- * @param[in] nMajorType     The CBOR major type (0 or 1).
- * @param[in] uArgument      The argument from the head.
- * @param[out] pDecodedItem  The filled in decoded item.
+ * @param[in] nMajorType       The CBOR major type (0 or 1).
+ * @param[in] uArgument        The argument from the head.
+ * @param[in] nAdditionalInfo  So it can be error-checked.
+ * @param[out] pDecodedItem    The filled in decoded item.
  *
- * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered
+ * @retval QCBOR_ERR_INT_OVERFLOW  Too-large negative encountered.
+ * @retval QCBOR_ERR_BAD_INT       nAdditionalInfo indicated indefinte.
  *
  * Must only be called when major type is 0 or 1.
  *
@@ -865,10 +873,16 @@
 static QCBORError
 QCBOR_Private_DecodeInteger(const int      nMajorType,
                             const uint64_t uArgument,
+                            const int      nAdditionalInfo,
                             QCBORItem     *pDecodedItem)
 {
    QCBORError uReturn = QCBOR_SUCCESS;
 
+   if(nAdditionalInfo == LEN_IS_INDEFINITE) {
+      uReturn = QCBOR_ERR_BAD_INT;
+      goto Done;
+   }
+
    if(nMajorType == CBOR_MAJOR_TYPE_POSITIVE_INT) {
       if (uArgument <= INT64_MAX) {
          pDecodedItem->val.int64 = (int64_t)uArgument;
@@ -897,10 +911,234 @@
       }
    }
 
+Done:
    return uReturn;
 }
 
 
+/**
+ * @brief Decode text and byte strings
+ *
+ * @param[in] pMe              Decoder context.
+ * @param[in] bAllocate        Whether to allocate and copy string.
+ * @param[in] nMajorType       Whether it is a byte or text string.
+ * @param[in] uStrLen          The length of the string.
+ * @param[in] nAdditionalInfo  Whether it is an indefinite-length string.
+ * @param[out] pDecodedItem    The filled-in decoded item.
+ *
+ * @retval QCBOR_ERR_HIT_END          Unexpected end of input.
+ * @retval QCBOR_ERR_STRING_ALLOCATE  Out of memory.
+ * @retval QCBOR_ERR_STRING_TOO_LONG  String longer than SIZE_MAX - 4.
+ * @retval QCBOR_ERR_NO_STRING_ALLOCATOR  Allocation requested, but no allocator
+ *
+ * This reads @c uStrlen bytes from the input and fills in @c
+ * pDecodedItem. If @c bAllocate is true, then memory for the string
+ * is allocated.
+ */
+static QCBORError
+QCBOR_Private_DecodeString(QCBORDecodeContext  *pMe,
+                           const bool           bAllocate,
+                           const int            nMajorType,
+                           const uint64_t       uStrLen,
+                           const int            nAdditionalInfo,
+                           QCBORItem           *pDecodedItem)
+{
+   QCBORError uReturn = QCBOR_SUCCESS;
+
+   /* ---- Figure out the major type ---- */
+   #if CBOR_MAJOR_TYPE_BYTE_STRING + 4 != QCBOR_TYPE_BYTE_STRING
+   #error QCBOR_TYPE_BYTE_STRING not lined up with major type
+   #endif
+
+   #if CBOR_MAJOR_TYPE_TEXT_STRING + 4 != QCBOR_TYPE_TEXT_STRING
+   #error QCBOR_TYPE_TEXT_STRING not lined up with major type
+   #endif
+   pDecodedItem->uDataType = (uint8_t)(nMajorType + 4);
+
+   if(nAdditionalInfo == LEN_IS_INDEFINITE) {
+      /* --- Just the head of an indefinite-length string --- */
+      pDecodedItem->val.string = (UsefulBufC){NULL, QCBOR_STRING_LENGTH_INDEFINITE};
+
+   } else {
+      /* --- A definite-length string --- */
+      /* --- (which might be a chunk of an indefinte-length string) --- */
+
+      /* CBOR lengths can be 64 bits, but size_t is not 64 bits on all
+       * CPUs.  This check makes the casts to size_t below safe.
+       *
+       * The max is 4 bytes less than the largest sizeof() so this can be
+       * tested by putting a SIZE_MAX length in the CBOR test input (no
+       * one will care the limit on strings is 4 bytes shorter).
+       */
+      if(uStrLen > SIZE_MAX-4) {
+         uReturn = QCBOR_ERR_STRING_TOO_LONG;
+         goto Done;
+      }
+
+      const UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(&(pMe->InBuf), (size_t)uStrLen);
+      if(UsefulBuf_IsNULLC(Bytes)) {
+         /* Failed to get the bytes for this string item */
+         uReturn = QCBOR_ERR_HIT_END;
+         goto Done;
+      }
+
+      if(bAllocate) {
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
+         /* --- Put string in allocated memory --- */
+
+         /* Note that this is not where allocation to coalesce
+          * indefinite-length strings is done. This is for when the
+          * caller has requested all strings be allocated. Disabling
+          * indefinite length strings also disables this allocate-all
+          * option.
+          */
+
+         if(pMe->StringAllocator.pfAllocator == NULL) {
+            uReturn = QCBOR_ERR_NO_STRING_ALLOCATOR;
+            goto Done;
+         }
+         UsefulBuf NewMem = StringAllocator_Allocate(&(pMe->StringAllocator), (size_t)uStrLen);
+         if(UsefulBuf_IsNULL(NewMem)) {
+            uReturn = QCBOR_ERR_STRING_ALLOCATE;
+            goto Done;
+         }
+         pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes);
+         pDecodedItem->uDataAlloc = 1;
+#else
+         uReturn = QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED;
+#endif /*  ! QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
+      } else {
+         /* --- Normal case with no string allocator --- */
+         pDecodedItem->val.string = Bytes;
+      }
+   }
+
+Done:
+   return uReturn;
+}
+
+
+/**
+ * @brief Decode array or map.
+ *
+ * @param[in] uMode            Decoder mode.
+ * @param[in] nMajorType       Whether it is a byte or text string.
+ * @param[in] uItemCount       The length of the string.
+ * @param[in] nAdditionalInfo  Whether it is an indefinite-length.
+ * @param[out] pDecodedItem    The filled-in decoded item.
+ *
+ * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinites disabled.
+ * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG     Too many items in array/map.
+ *
+ * Not much to do for arrays and maps. Just the type item count (but a
+ * little messy because of ifdefs for indefinite-lengths and
+ * map-as-array decoding).
+ *
+ * This also does the bulk of the work for @ref
+ * QCBOR_DECODE_MODE_MAP_AS_ARRAY, a special mode to handle
+ * arbitrarily complex map labels. This ifdefs out with
+ * QCBOR_DISABLE_NON_INTEGER_LABELS.
+ */
+static QCBORError
+QCBOR_Private_DecodeArrayOrMap(const uint8_t  uMode,
+                               const int      nMajorType,
+                               const uint64_t uItemCount,
+                               const int      nAdditionalInfo,
+                               QCBORItem     *pDecodedItem)
+{
+   QCBORError uReturn;
+
+   /* ------ Sort out the data type ------ */
+   #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
+   pDecodedItem->uDataType = (uint8_t)nMajorType;
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+   if(uMode == QCBOR_DECODE_MODE_MAP_AS_ARRAY && nMajorType == QCBOR_TYPE_MAP) {
+      pDecodedItem->uDataType = QCBOR_TYPE_MAP_AS_ARRAY;
+   }
+#else
+   (void)uMode;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
+   uReturn = QCBOR_SUCCESS;
+
+   if(nAdditionalInfo == LEN_IS_INDEFINITE) {
+      /* ------ Indefinite-length array/map ----- */
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+      pDecodedItem->val.uCount = QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH;
+#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+      uReturn = QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED;
+#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+   } else {
+
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+      if(uMode == QCBOR_DECODE_MODE_MAP_AS_ARRAY && nMajorType == QCBOR_TYPE_MAP) {
+         /* ------ Definite-length map as array ------ */
+
+         if(uItemCount > QCBOR_MAX_ITEMS_IN_ARRAY/2) {
+            uReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
+         } else {
+            /* cast OK because of check above */
+            pDecodedItem->val.uCount = (uint16_t)uItemCount*2;
+         }
+
+      } else
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+      {
+         /* ------ Definite-length array/map ------ */
+         if(uItemCount > QCBOR_MAX_ITEMS_IN_ARRAY) {
+            uReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
+         } else {
+            /* cast OK because of check above */
+            pDecodedItem->val.uCount = (uint16_t)uItemCount;
+         }
+      }
+   }
+
+   return uReturn;
+}
+
+
+/**
+ * @brief Decode a tag number.
+ *
+ * @param[in] uTagNumber       The length of the string.
+ * @param[in] nAdditionalInfo  So this can be error-checked.
+ * @param[out] pDecodedItem    The filled-in decoded item.
+ *
+ * @retval QCBOR_ERR_BAD_INT        nAdditionalInfo is LEN_IS_INDEFINITE.
+ * @retval QCBOR_ERR_TAGS_DISABLED  QCBOR_DISABLE_TAGS is defined.
+ *
+ * Not much to do for tags, but fill in pDecodedItem and check for
+ * error in nAdditionalInfo.
+ */
+static QCBORError
+QCBOR_Private_DecodeTag(const uint64_t uTagNumber,
+                        const int      nAdditionalInfo,
+                        QCBORItem     *pDecodedItem)
+{
+#ifndef QCBOR_DISABLE_TAGS
+   if(nAdditionalInfo == LEN_IS_INDEFINITE) {
+      return QCBOR_ERR_BAD_INT;
+   } else {
+      pDecodedItem->val.uTagV = uTagNumber;
+      pDecodedItem->uDataType = QCBOR_TYPE_TAG;
+      return QCBOR_SUCCESS;
+   }
+#else /* QCBOR_DISABLE_TAGS */
+   (void)nAdditionalInfo;
+   (void)uArgument;
+   (void)pDecodedItem;
+   return QCBOR_ERR_TAGS_DISABLED;
+#endif /* QCBOR_DISABLE_TAGS */
+}
+
+
 /* Make sure #define value line up as DecodeSimple counts on this. */
 #if QCBOR_TYPE_FALSE != CBOR_SIMPLEV_FALSE
 #error QCBOR_TYPE_FALSE macro value wrong
@@ -1050,129 +1288,11 @@
 
 
 /**
- * @brief Decode text and byte strings
- *
- * @param[in] pAllocator     The string allocator or NULL.
- * @param[in] uStrLen        The length of the string.
- * @param[in] pUInBuf        The surce from which to read the string's bytes.
- * @param[out] pDecodedItem  The filled in decoded item.
- *
- * @retval QCBOR_ERR_HIT_END          Unexpected end of input.
- * @retval QCBOR_ERR_STRING_ALLOCATE  Out of memory.
- * @retval QCBOR_ERR_STRING_TOO_LONG  String longer than SIZE_MAX - 4.
- *
- * The reads @c uStrlen bytes from @c pUInBuf and fills in @c
- * pDecodedItem. If @c pAllocator is not NULL then memory for the
- * string is allocated.
- */
-static QCBORError
-QCBOR_Private_DecodeBytes(const QCBORInternalAllocator *pAllocator,
-                          const uint64_t                uStrLen,
-                          UsefulInputBuf               *pUInBuf,
-                          QCBORItem                    *pDecodedItem)
-{
-   QCBORError uReturn = QCBOR_SUCCESS;
-
-   /* CBOR lengths can be 64 bits, but size_t is not 64 bits on all
-    * CPUs.  This check makes the casts to size_t below safe.
-    *
-    * The max is 4 bytes less than the largest sizeof() so this can be
-    * tested by putting a SIZE_MAX length in the CBOR test input (no
-    * one will care the limit on strings is 4 bytes shorter).
-    */
-   if(uStrLen > SIZE_MAX-4) {
-      uReturn = QCBOR_ERR_STRING_TOO_LONG;
-      goto Done;
-   }
-
-   const UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(pUInBuf, (size_t)uStrLen);
-   if(UsefulBuf_IsNULLC(Bytes)) {
-      /* Failed to get the bytes for this string item */
-      uReturn = QCBOR_ERR_HIT_END;
-      goto Done;
-   }
-
-#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
-   /* Note that this is not where allocation to coalesce
-    * indefinite-length strings is done. This is for when the caller
-    * has requested all strings be allocated. Disabling indefinite
-    * length strings also disables this allocate-all option.
-    */
-   if(pAllocator) {
-      /* request to use the string allocator to make a copy */
-      UsefulBuf NewMem = StringAllocator_Allocate(pAllocator, (size_t)uStrLen);
-      if(UsefulBuf_IsNULL(NewMem)) {
-         uReturn = QCBOR_ERR_STRING_ALLOCATE;
-         goto Done;
-      }
-      pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes);
-      pDecodedItem->uDataAlloc = 1;
-      goto Done;
-   }
-#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
-   (void)pAllocator;
-#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
-
-   /* Normal case with no string allocator */
-   pDecodedItem->val.string = Bytes;
-
-Done:
-   return uReturn;
-}
-
-
-/**
- * @brief Map the CBOR major types for strings to the QCBOR types.
- *
- * @param[in] nCBORMajorType  The CBOR major type to convert.
- * @retturns QCBOR type number.
- *
- * This only works for the two string types.
- */
-static uint8_t
-QCBOR_Private_ConvertStringMajorTypes(int nCBORMajorType)
-{
-   #if CBOR_MAJOR_TYPE_BYTE_STRING + 4 != QCBOR_TYPE_BYTE_STRING
-   #error QCBOR_TYPE_BYTE_STRING no lined up with major type
-   #endif
-
-   #if CBOR_MAJOR_TYPE_TEXT_STRING + 4 != QCBOR_TYPE_TEXT_STRING
-   #error QCBOR_TYPE_TEXT_STRING no lined up with major type
-   #endif
-
-   return (uint8_t)(nCBORMajorType + 4);
-}
-
-
-/**
- * @brief Map the CBOR major types  for arrays/maps  to the QCBOR types.
- *
- * @param[in] nCBORMajorType  The CBOR major type to convert.
- * @retturns QCBOR type number.
- *
- * This only works for the two aggregate types.
- */
-static uint8_t
-QCBORDecode_Private_ConvertArrayOrMapType(int nCBORMajorType)
-{
-   #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
-
-   return (uint8_t)(nCBORMajorType);
-}
-
-
-/**
  * @brief Decode a single primitive data item (decode layer 6).
  *
- * @param[in] pUInBuf       Input buffer to read data item from.
- * @param[out] pDecodedItem  The filled-in decoded item.
- * @param[in] pAllocator    The allocator to use for strings or NULL.
+ * @param[in] pMe                Decoder context.
+ * @param[in] bAllocateStrings   If true, use allocator for strings.
+ * @param[out] pDecodedItem      The filled-in decoded item.
  *
  * @retval QCBOR_ERR_UNSUPPORTED             Encountered unsupported/reserved
  *                                           features
@@ -1180,6 +1300,7 @@
  * @retval QCBOR_ERR_INT_OVERFLOW            Too-large negative encountered
  * @retval QCBOR_ERR_STRING_ALLOCATE         Out of memory.
  * @retval QCBOR_ERR_STRING_TOO_LONG         String longer than SIZE_MAX - 4.
+ * @retval QCBOR_ERR_NO_STRING_ALLOCATOR     Allocation requested, but no allocator
  * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode
  *                                           of half-precision disabled
  * @retval QCBOR_ERR_ALL_FLOAT_DISABLED      Float-point in input, but all
@@ -1189,104 +1310,67 @@
  * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED  Indefinite length map/array
  *                                              in input, but indefinite
  *                                              lengths disabled.
+ * @retval QCBOR_ERR_BAD_INT                 nAdditionalInfo indicated indefinte.
+ * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG   Too many items in array/map.
+ * @retval QCBOR_ERR_TAGS_DISABLED           QCBOR_DISABLE_TAGS is defined.
  *
- * This decodes the most primitive / atomic data item. It does
- * no combing of data items.
+ * This decodes the most primitive/atomic data item. It does no
+ * combining of data items.
  */
 static QCBORError
-QCBOR_Private_DecodeAtomicDataItem(UsefulInputBuf               *pUInBuf,
-                                   QCBORItem                    *pDecodedItem,
-                                   const QCBORInternalAllocator *pAllocator)
+QCBOR_Private_DecodeAtomicDataItem(QCBORDecodeContext  *pMe,
+                                   const bool           bAllocateStrings,
+                                   QCBORItem           *pDecodedItem)
 {
    QCBORError uReturn;
-
-   /* Get the major type and the argument. The argument 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      nMajorType = 0;
    uint64_t uArgument = 0;
    int      nAdditionalInfo = 0;
 
-   memset(pDecodedItem, 0, sizeof(QCBORItem));
-
-   uReturn = QCBOR_Private_DecodeHead(pUInBuf, &nMajorType, &uArgument, &nAdditionalInfo);
-   if(uReturn) {
-      goto Done;
+   /* Decode the "head" that every CBOR item has into the major type,
+    * argument and the additional info.
+    */
+   uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf), &nMajorType, &uArgument, &nAdditionalInfo);
+   if(uReturn != QCBOR_SUCCESS) {
+      return uReturn;
    }
 
-   /* At this point the major type and the argument are valid. We've
-    * got the type and the argument that starts every CBOR data item.
+   memset(pDecodedItem, 0, sizeof(QCBORItem));
+
+   /* All the functions below get inlined by the optimizer. This code
+    * is easier to read with them all being similar functions, even if
+    * some functions don't do much.
     */
    switch (nMajorType) {
       case CBOR_MAJOR_TYPE_POSITIVE_INT: /* Major type 0 */
       case CBOR_MAJOR_TYPE_NEGATIVE_INT: /* Major type 1 */
-         if(nAdditionalInfo == LEN_IS_INDEFINITE) {
-            uReturn = QCBOR_ERR_BAD_INT;
-         } else {
-            uReturn = QCBOR_Private_DecodeInteger(nMajorType, uArgument, pDecodedItem);
-         }
+         return QCBOR_Private_DecodeInteger(nMajorType, uArgument, nAdditionalInfo, pDecodedItem);
          break;
 
       case CBOR_MAJOR_TYPE_BYTE_STRING: /* Major type 2 */
       case CBOR_MAJOR_TYPE_TEXT_STRING: /* Major type 3 */
-         pDecodedItem->uDataType = QCBOR_Private_ConvertStringMajorTypes(nMajorType);
-         if(nAdditionalInfo == LEN_IS_INDEFINITE) {
-            pDecodedItem->val.string = (UsefulBufC){NULL, QCBOR_STRING_LENGTH_INDEFINITE};
-         } else {
-            uReturn = QCBOR_Private_DecodeBytes(pAllocator, uArgument, pUInBuf, pDecodedItem);
-         }
+         return QCBOR_Private_DecodeString(pMe, bAllocateStrings, nMajorType, uArgument, nAdditionalInfo, pDecodedItem);
          break;
 
       case CBOR_MAJOR_TYPE_ARRAY: /* Major type 4 */
       case CBOR_MAJOR_TYPE_MAP:   /* Major type 5 */
-         if(nAdditionalInfo == LEN_IS_INDEFINITE) {
-            /* Indefinite-length string. */
-#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
-            pDecodedItem->val.uCount = QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH;
-#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
-            uReturn = QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED;
-            break;
-#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
-         } else {
-            /* Definite-length string. */
-            if(uArgument > QCBOR_MAX_ITEMS_IN_ARRAY) {
-                uReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
-                goto Done;
-             }
-            /* cast OK because of check above */
-            pDecodedItem->val.uCount = (uint16_t)uArgument;
-         }
-         pDecodedItem->uDataType = QCBORDecode_Private_ConvertArrayOrMapType(nMajorType);
+         return QCBOR_Private_DecodeArrayOrMap(pMe->uDecodeMode, nMajorType, uArgument, nAdditionalInfo, pDecodedItem);
          break;
 
       case CBOR_MAJOR_TYPE_TAG: /* Major type 6, tag numbers */
-#ifndef QCBOR_DISABLE_TAGS
-         if(nAdditionalInfo == LEN_IS_INDEFINITE) {
-            uReturn = QCBOR_ERR_BAD_INT;
-         } else {
-            pDecodedItem->val.uTagV = uArgument;
-            pDecodedItem->uDataType = QCBOR_TYPE_TAG;
-         }
-#else /* QCBOR_DISABLE_TAGS */
-         uReturn = QCBOR_ERR_TAGS_DISABLED;
-#endif /* QCBOR_DISABLE_TAGS */
+         return QCBOR_Private_DecodeTag(uArgument, nAdditionalInfo, pDecodedItem);
          break;
 
       case CBOR_MAJOR_TYPE_SIMPLE:
          /* Major type 7: float, double, true, false, null... */
-         uReturn = QCBOR_Private_DecodeType7(nAdditionalInfo, uArgument, pDecodedItem);
+         return QCBOR_Private_DecodeType7(nAdditionalInfo, uArgument, pDecodedItem);
          break;
 
       default:
          /* Never happens because DecodeHead() should never return > 7 */
-         uReturn = QCBOR_ERR_UNSUPPORTED;
+         return QCBOR_ERR_UNSUPPORTED;
          break;
    }
-
-Done:
-   return uReturn;
 }
 
 
@@ -1338,57 +1422,53 @@
     *   QCBORItem                                     56          52
     *   TOTAL                                        120          74
     */
-
-   /* The string allocator is used here for two purposes: 1)
-    * coalescing the chunks of an indefinite-length string, 2)
-    * allocating storage for every string returned when requested.
-    *
-    * The first use is below in this function. Indefinite-length
-    * strings cannot be processed at all without a string allocator.
-    *
-    * The second used is in DecodeBytes() which is called by
-    * GetNext_Item() below. This second use unneccessary for most use
-    * and only happens when requested in the call to
-    * QCBORDecode_SetMemPool(). If the second use not requested then
-    * NULL is passed for the string allocator to GetNext_Item().
-    *
-    * QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS disables the string
-    * allocator altogether and thus both of these uses. It reduced the
-    * decoder object code by about 400 bytes.
-    */
-   const QCBORInternalAllocator *pAllocatorForGetNext = NULL;
-
-#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
-   const QCBORInternalAllocator *pAllocator = NULL;
-
-   if(pMe->StringAllocator.pfAllocator) {
-      pAllocator = &(pMe->StringAllocator);
-      if(pMe->bStringAllocateAll) {
-         pAllocatorForGetNext = pAllocator;
-      }
-   }
-#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
-
    QCBORError uReturn;
-   uReturn = QCBOR_Private_DecodeAtomicDataItem(&(pMe->InBuf), pDecodedItem, pAllocatorForGetNext);
+
+   /* A note about string allocation -- Memory for strings is
+    * allocated either because 1) indefinte-length string chunks are
+    * being coalecsed or 2) caller has requested all strings be
+    * allocated.  The first case is handed below here. The second case
+    * is handled in DecodeString if the bAllocate is true. That
+    * boolean originates here with pMe->bStringAllocateAll immediately
+    * below. That is, QCBOR_Private_DecodeAtomicDataItem() is called
+    * in two different contexts here 1) main-line processing which is
+    * where definite-length strings need to be allocated if
+    * bStringAllocateAll is true and 2) processing chunks of
+    * indefinite-lengths strings in in which case there must be no
+    * allocation.
+    */
+
+
+   uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, pMe->bStringAllocateAll, pDecodedItem);
    if(uReturn != QCBOR_SUCCESS) {
       goto Done;
    }
 
-   /* Only do indefinite-length processing on strings */
-   const uint8_t uStringType = pDecodedItem->uDataType;
-   if(uStringType!= QCBOR_TYPE_BYTE_STRING && uStringType != QCBOR_TYPE_TEXT_STRING) {
+
+   /* This is where out-of-place break is detected for the whole
+    * decoding stack. Break is an error for everything that calls
+    * QCBORDecode_Private_GetNextFullString(), so the check is
+    * centralized here.
+    */
+   if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+      uReturn = QCBOR_ERR_BAD_BREAK;
       goto Done;
    }
 
-   /* Is this a string with an indefinite length? */
+
+   /* Skip out if not an indefinite-length string */
+   const uint8_t uStringType = pDecodedItem->uDataType;
+   if(uStringType != QCBOR_TYPE_BYTE_STRING &&
+      uStringType != QCBOR_TYPE_TEXT_STRING) {
+      goto Done;
+   }
    if(pDecodedItem->val.string.len != QCBOR_STRING_LENGTH_INDEFINITE) {
       goto Done;
    }
 
 #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
    /* Can't decode indefinite-length strings without a string allocator */
-   if(pAllocator == NULL) {
+   if(!pMe->StringAllocator.pfAllocator) {
       uReturn = QCBOR_ERR_NO_STRING_ALLOCATOR;
       goto Done;
    }
@@ -1399,12 +1479,12 @@
    for(;;) {
       /* Get QCBORItem for next chunk */
       QCBORItem StringChunkItem;
-      /* Pass a NULL string allocator to GetNext_Item() because the
-       * individual string chunks in an indefinite-length should not
-       * be allocated. They are always copied in the the contiguous
-       * buffer allocated here.
+      /* Pass false to DecodeAtomicDataItem() because the individual
+       * string chunks in an indefinite-length must not be
+       * allocated. They are always copied into the allocated
+       * contiguous buffer allocated here.
        */
-      uReturn = QCBOR_Private_DecodeAtomicDataItem(&(pMe->InBuf), &StringChunkItem, NULL);
+      uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, false, &StringChunkItem);
       if(uReturn) {
          break;
       }
@@ -1433,10 +1513,9 @@
           * equivalent to StringAllocator_Allocate(). Subsequently it is
           * not NULL and a reallocation happens.
           */
-         UsefulBuf NewMem = StringAllocator_Reallocate(pAllocator,
+         UsefulBuf NewMem = StringAllocator_Reallocate(&(pMe->StringAllocator),
                                                        FullString.ptr,
                                                        FullString.len + StringChunkItem.val.string.len);
-
          if(UsefulBuf_IsNULL(NewMem)) {
             uReturn = QCBOR_ERR_STRING_ALLOCATE;
             break;
@@ -1449,7 +1528,7 @@
 
    if(uReturn != QCBOR_SUCCESS && !UsefulBuf_IsNULLC(FullString)) {
       /* Getting the item failed, clean up the allocated memory */
-      StringAllocator_Free(pAllocator, FullString.ptr);
+      StringAllocator_Free(&(pMe->StringAllocator), FullString.ptr);
    }
 #else /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
    uReturn = QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED;
@@ -1677,96 +1756,83 @@
  * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG   Too many items in array.
  * @retval QCBOR_ERR_MAP_LABEL_TYPE          Map label not string or integer.
  *
- * If a the current nesting level is a map, then this
- * combines pairs of items into one data item with a label
- * and value.
+ * If the current nesting level is a map, then this combines pairs of
+ * items into one data item with a label and value.
  *
- * This is passthrough if the current nesting level is
- * not a map.
+ * This is passthrough if the current nesting level is not a map.
  *
- * This also implements maps-as-array mode where a map
- * is treated like an array to allow caller to do their
- * own label processing.
+ * This also implements maps-as-array mode where a map is treated like
+ * an array to allow caller to do their own label processing.
  */
+
 static QCBORError
 QCBORDecode_Private_GetNextMapEntry(QCBORDecodeContext *pMe,
                                     QCBORItem          *pDecodedItem)
 {
-   QCBORError uReturn = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
-   if(uReturn != QCBOR_SUCCESS) {
+   QCBORItem  LabelItem;
+   QCBORError uErr;
+
+   uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
+   if(QCBORDecode_IsUnrecoverableError(uErr)) {
       goto Done;
    }
 
-   if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
-      /* Break can't be a map entry */
+   if(!DecodeNesting_IsCurrentTypeMap(&(pMe->nesting))) {
+      /* Not decoding a map. Nothing to do. */
+      /* When decoding maps-as-arrays, the type will be
+       * QCBOR_TYPE_MAP_AS_ARRAY and this function will exit
+       * here. This is now map processing for maps-as-arrays is not
+       * done. */
       goto Done;
    }
 
-   if(pMe->uDecodeMode != QCBOR_DECODE_MODE_MAP_AS_ARRAY) {
-      /* Normal decoding of maps -- combine label and value into one item. */
+   /* Decoding a map entry, so the item decoded above was the label */
+   LabelItem = *pDecodedItem;
 
-      if(DecodeNesting_IsCurrentTypeMap(&(pMe->nesting))) {
-         /* Save label in pDecodedItem and get the next which will
-          * be the real data item.
-          */
-         QCBORItem LabelItem = *pDecodedItem;
-         uReturn = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
-         if(QCBORDecode_IsUnrecoverableError(uReturn)) {
-            goto Done;
-         }
+   /* Get the value of the map item */
+   uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
+   if(QCBORDecode_IsUnrecoverableError(uErr)) {
+      goto Done;
+   }
 
-         pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
+   /* Combine the label item and value item into one */
+   pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
+   pDecodedItem->uLabelType  = LabelItem.uDataType;
 
-         if(LabelItem.uDataType == QCBOR_TYPE_TEXT_STRING) {
-            /* strings are always good labels */
-            pDecodedItem->label.string = LabelItem.val.string;
-            pDecodedItem->uLabelType = QCBOR_TYPE_TEXT_STRING;
-         } else if (QCBOR_DECODE_MODE_MAP_STRINGS_ONLY == pMe->uDecodeMode) {
-            /* It's not a string and we only want strings */
-            uReturn = QCBOR_ERR_MAP_LABEL_TYPE;
-            goto Done;
-         } 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) {
-            pDecodedItem->label.uint64 = LabelItem.val.uint64;
-            pDecodedItem->uLabelType = QCBOR_TYPE_UINT64;
-         } else if(LabelItem.uDataType == QCBOR_TYPE_BYTE_STRING) {
-            pDecodedItem->label.string = LabelItem.val.string;
-            pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
-            pDecodedItem->uLabelType = QCBOR_TYPE_BYTE_STRING;
-         } else {
-            /* label is not an int or a string. It is an arrray
-             * or a float or such and this implementation doesn't handle that.
-             * Also, tags on labels are ignored.
-             */
-            uReturn = QCBOR_ERR_MAP_LABEL_TYPE;
-            goto Done;
-         }
-      }
-   } else {
-      /* Decoding of maps as arrays to let the caller decide what to do
-       * about labels, particularly lables that are not integers or
-       * strings.
-       */
-      if(pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
-         pDecodedItem->uDataType = QCBOR_TYPE_MAP_AS_ARRAY;
-         if(pDecodedItem->val.uCount != UINT16_MAX) {
-            /* Adjust definite-length map item count */
-            if(pDecodedItem->val.uCount > QCBOR_MAX_ITEMS_IN_ARRAY/2) {
-               uReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
-               goto Done;
-            }
-            /* Cast is safe because of check against QCBOR_MAX_ITEMS_IN_ARRAY/2.
-             * Cast is needed because of integer promotion.
-             */
-            pDecodedItem->val.uCount = (uint16_t)(pDecodedItem->val.uCount * 2);
-         }
-      }
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+   /* QCBOR_DECODE_MODE_MAP_STRINGS_ONLY might have been a bad idea. Maybe
+    * get rid of it in QCBOR 2.0
+    */
+   if(pMe->uDecodeMode == QCBOR_DECODE_MODE_MAP_STRINGS_ONLY &&
+      LabelItem.uDataType != QCBOR_TYPE_TEXT_STRING) {
+      uErr = QCBOR_ERR_MAP_LABEL_TYPE;
+      goto Done;
+   }
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
+   switch(LabelItem.uDataType) {
+      case QCBOR_TYPE_INT64:
+         pDecodedItem->label.int64 = LabelItem.val.int64;
+         break;
+
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+      case QCBOR_TYPE_UINT64:
+         pDecodedItem->label.uint64 = LabelItem.val.uint64;
+         break;
+
+      case QCBOR_TYPE_TEXT_STRING:
+      case QCBOR_TYPE_BYTE_STRING:
+         pDecodedItem->label.string = LabelItem.val.string;
+         break;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
+      default:
+         uErr = QCBOR_ERR_MAP_LABEL_TYPE;
+         goto Done;
    }
 
 Done:
-   return uReturn;
+   return uErr;
 }
 
 
@@ -1774,7 +1840,7 @@
 /**
  * @brief Peek and see if next data item is a break;
  *
- * @param[in]  pUIB            UsefulInputBuf to read from.
+ * param[in]  pUIB            UsefulInputBuf to read from.
  * @param[out] pbNextIsBreak   Indicate if next was a break or not.
  *
  * @return  Any decoding error.
@@ -1783,19 +1849,19 @@
  * if not it is not consumed.
 */
 static QCBORError
-QCBOR_Private_NextIsBreak(UsefulInputBuf *pUIB, bool *pbNextIsBreak)
+QCBOR_Private_NextIsBreak(QCBORDecodeContext *pMe, bool *pbNextIsBreak)
 {
    *pbNextIsBreak = false;
-   if(UsefulInputBuf_BytesUnconsumed(pUIB) != 0) {
+   if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf)) != 0) {
       QCBORItem Peek;
-      size_t uPeek = UsefulInputBuf_Tell(pUIB);
-      QCBORError uReturn = QCBOR_Private_DecodeAtomicDataItem(pUIB, &Peek, NULL);
+      size_t uPeek = UsefulInputBuf_Tell(&(pMe->InBuf));
+      QCBORError uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, false, &Peek);
       if(uReturn != QCBOR_SUCCESS) {
          return uReturn;
       }
       if(Peek.uDataType != QCBOR_TYPE_BREAK) {
          /* It is not a break, rewind so it can be processed normally. */
-         UsefulInputBuf_Seek(pUIB, uPeek);
+         UsefulInputBuf_Seek(&(pMe->InBuf), uPeek);
       } else {
          *pbNextIsBreak = true;
       }
@@ -1863,7 +1929,7 @@
 
          /* Check for a break which is what ends indefinite-length arrays/maps */
          bool bIsBreak = false;
-         uReturn = QCBOR_Private_NextIsBreak(&(pMe->InBuf), &bIsBreak);
+         uReturn = QCBOR_Private_NextIsBreak(pMe, &bIsBreak);
          if(uReturn != QCBOR_SUCCESS) {
             goto Done;
          }
@@ -1994,14 +2060,6 @@
       goto Done;
    }
 
-   /* Breaks ending arrays/maps are processed later in the call to
-    * QCBORDecode_NestLevelAscender(). They should never show up here.
-    */
-   if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
-      uReturn = QCBOR_ERR_BAD_BREAK;
-      goto Done;
-   }
-
    /* Record the nesting level for this data item before processing
     * any of decrementing and descending.
     */
@@ -3511,6 +3569,7 @@
       return;
    }
 
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    QCBORItem OneItemSeach[2];
    OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
    OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
@@ -3528,8 +3587,16 @@
    }
 
    *pItem = OneItemSeach[0];
-
 Done:
+
+#else
+   (void)pMe;
+   (void)szLabel;
+   (void)uQcborType;
+   (void)pItem;
+   QCBORError uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
    pMe->uLastError = (uint8_t)uReturn;
 }
 
@@ -3580,9 +3647,11 @@
    }
 
    uint8_t uItemDataType = pItem->uDataType;
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) {
       uItemDataType = QCBOR_TYPE_ARRAY;
    }
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 
    if(uItemDataType != uType) {
       pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
@@ -3999,6 +4068,7 @@
 void
 QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel)
 {
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    QCBORItem OneItemSeach[2];
    OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
    OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
@@ -4006,6 +4076,10 @@
    OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE;
 
    QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach);
+#else
+   (void)szLabel;
+   pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 }
 
 /*
@@ -4029,6 +4103,7 @@
 void
 QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel)
 {
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    QCBORItem OneItemSeach[2];
    OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
    OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
@@ -4036,6 +4111,10 @@
    OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE;
 
    QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach);
+#else
+   (void)szLabel;
+   pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 }
 
 
@@ -4071,9 +4150,13 @@
    }
 
    uint8_t uItemDataType = Item.uDataType;
+
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY ) {
       uItemDataType = QCBOR_TYPE_ARRAY;
    }
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
    if(uItemDataType != uType) {
       uErr = QCBOR_ERR_UNEXPECTED_TYPE;
       goto Done;