Revise tag handling; breaks compatibility with QCBOR v1

This is the big tag processing revision that breaks backwards compatibility.

QCBOREncode_AddTag()->QCBOREncode_AddTagNumber()

Pluggable tag content decoders are added.

QCBORDecode_GetTagNumber() added

QCBORDecode_NthTagNumber() added; it replaces QCBORDecode_GetNthTagNum()

New file qcbor_tag_decode for the tag content decoders

New tag decode example file

Incompatible changes

all tag content decoders are not loaded automatically
unprocessed tag numbers give an error
v1 compatibility mode available to restore compatibility

Add Seek to label in map QCBORDecode_SeekToLabelN, QCBORDecode_SeekToLabelSZ

Remove some deprecated tag functions from a very long time ago. They didn't work well and probably weren't used. QCBORDecode_SetCallerConfiguredTagList() QCBORDecode_GetNextWithTags(), QCBORDecode_IsTagged()

While all tests are passing, this is not providing adequate test coverage for the new functionality yet.

* Revise tag handling (check point, not tested)

* Reverse order of tag nums in QCBORItem to reduce code size

* Update tag decoding documentation

* Replaces IsTagged with MatchOneTagNumber; get rid of ancient tag list stuff

* fix & improve QCBOR_DISABLE_TAGS

* straggler from last commit

* remove qcbor2_encode.h

* Big check point

* Another checkpoint

* Update CMAKE files

* Add missing tag-example files

* Check point (getting close)

* Add tag examples

* Check point; tests passing, TagSpec removed

* Remove unused; test fan out might pass

* Rearrange v1 compat set up; other clean up

* fix test fan out, lots of nits

* Address a number of TODOs

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 21845f4..5edd2d6 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -34,6 +34,7 @@
 
 #include "qcbor/qcbor_decode.h"
 #include "qcbor/qcbor_spiffy_decode.h"
+#include "qcbor/qcbor_tag_decode.h"
 #include "ieee754.h" /* Does not use math.h */
 
 #ifndef QCBOR_DISABLE_FLOAT_HW_USE
@@ -666,9 +667,26 @@
    /* Inialize me->auMappedTags to CBOR_TAG_INVALID16. See
     * GetNext_TaggedItem() and MapTagNumber(). */
    memset(pMe->auMappedTags, 0xff, sizeof(pMe->auMappedTags));
+
+   pMe->uTagNumberCheckOffset = SIZE_MAX;
 }
 
 
+/*
+ * Public function, see header file
+ */
+void
+QCBORDecode_CompatibilityV1(QCBORDecodeContext *pMe)
+{
+   pMe->uDecodeMode |= QCBOR_DECODE_UNPROCESSED_TAG_NUMBERS;
+#ifndef QCBOR_DISABLE_TAGS
+   QCBORDecode_InstallTagDecoders(pMe, QCBORDecode_TagDecoderTablev1, NULL);
+#endif /* ! QCBOR_DISABLE_TAGS */
+}
+
+
+
+
 #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
 
 /*
@@ -689,19 +707,6 @@
 
 
 
-/*
- * Deprecated public function, see header file
- */
-void
-QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext   *pMe,
-                                       const QCBORTagListIn *pTagList)
-{
-   /* This does nothing now. It is retained for backwards compatibility */
-   (void)pMe;
-   (void)pTagList;
-}
-
-
 
 
 /*
@@ -1049,7 +1054,7 @@
 /**
  * @brief Decode array or map.
  *
- * @param[in] uMode            Decoder mode.
+ * @param[in] uDecodeMode3Bit            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.
@@ -1068,7 +1073,7 @@
  * QCBOR_DISABLE_NON_INTEGER_LABELS.
  */
 static QCBORError
-QCBOR_Private_DecodeArrayOrMap(const uint8_t  uMode,
+QCBOR_Private_DecodeArrayOrMap(const uint8_t  uDecodeMode3Bit,
                                const int      nMajorType,
                                uint64_t       uItemCount,
                                const int      nAdditionalInfo,
@@ -1086,11 +1091,11 @@
    #endif
    pDecodedItem->uDataType = (uint8_t)nMajorType;
 #ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
-   if(uMode == QCBOR_DECODE_MODE_MAP_AS_ARRAY && nMajorType == QCBOR_TYPE_MAP) {
+   if(uDecodeMode3Bit == QCBOR_DECODE_MODE_MAP_AS_ARRAY && nMajorType == QCBOR_TYPE_MAP) {
       pDecodedItem->uDataType = QCBOR_TYPE_MAP_AS_ARRAY;
    }
 #else
-   (void)uMode;
+   (void)uDecodeMode3Bit;
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 
    uReturn = QCBOR_SUCCESS;
@@ -1109,7 +1114,7 @@
 
       } else {
 #ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
-         if(uMode == QCBOR_DECODE_MODE_MAP_AS_ARRAY && nMajorType == QCBOR_TYPE_MAP) {
+         if(uDecodeMode3Bit == QCBOR_DECODE_MODE_MAP_AS_ARRAY && nMajorType == QCBOR_TYPE_MAP) {
             /* ------ Map as array ------ */
             uItemCount *= 2;
          }
@@ -1138,16 +1143,16 @@
  * error in nAdditionalInfo.
  */
 static QCBORError
-QCBOR_Private_DecodeTag(const uint64_t uTagNumber,
-                        const int      nAdditionalInfo,
-                        QCBORItem     *pDecodedItem)
+QCBOR_Private_DecodeTagNumber(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;
+      pDecodedItem->val.uTagNumber = uTagNumber;
+      pDecodedItem->uDataType = QCBOR_TYPE_TAG_NUMBER;
       return QCBOR_SUCCESS;
    }
 #else /* QCBOR_DISABLE_TAGS */
@@ -1164,7 +1169,7 @@
 #if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
 
 static QCBORError
-QCBORDecode_Private_HalfConformance(const double d, const uint8_t uDecodeMode)
+QCBORDecode_Private_HalfConformance(const double d, const uint8_t uDecodeMode3Bit)
 {
    struct IEEE754_ToInt ToInt;
 
@@ -1178,7 +1183,7 @@
     * The only thing allowed here is a double/half-precision that
     * can't be converted to anything but a double.
     */
-   if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+   if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_DCBOR) {
       ToInt = IEEE754_DoubleToInt(d);
       if(ToInt.type != QCBOR_TYPE_DOUBLE) {
          return QCBOR_ERR_DCBOR_CONFORMANCE;
@@ -1190,12 +1195,12 @@
 
 
 static QCBORError
-QCBORDecode_Private_SingleConformance(const float f, const uint8_t uDecodeMode)
+QCBORDecode_Private_SingleConformance(const float f, const uint8_t uDecodeMode3Bit)
 {
    struct IEEE754_ToInt ToInt;
    IEEE754_union        ToSmaller;
 
-   if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+   if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_DCBOR) {
       /* See if it could have been encoded as an integer */
       ToInt = IEEE754_SingleToInt(f);
       if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) {
@@ -1209,7 +1214,7 @@
    }
 
    /* See if it could have been encoded shorter */
-   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+   if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_PREFERRED) {
       ToSmaller = IEEE754_SingleToHalf(f, true);
       if(ToSmaller.uSize != sizeof(float)) {
          return QCBOR_ERR_PREFERRED_CONFORMANCE;
@@ -1221,12 +1226,12 @@
 
 
 static QCBORError
-QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode)
+QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode3Bit)
 {
    struct IEEE754_ToInt ToInt;
    IEEE754_union        ToSmaller;
 
-   if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+   if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_DCBOR) {
       /* See if it could have been encoded as an integer */
       ToInt = IEEE754_DoubleToInt(d);
       if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) {
@@ -1237,9 +1242,9 @@
          return QCBOR_ERR_DCBOR_CONFORMANCE;
       }
    }
-   
+
    /* See if it could have been encoded shorter */
-   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+   if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_PREFERRED) {
       ToSmaller = IEEE754_DoubleToSmaller(d, true, true);
       if(ToSmaller.uSize != sizeof(double)) {
          return QCBOR_ERR_PREFERRED_CONFORMANCE;
@@ -1251,10 +1256,10 @@
 #else /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
 
 static QCBORError
-QCBORDecode_Private_SingleConformance(const float f, uint8_t uDecodeMode)
+QCBORDecode_Private_SingleConformance(const float f, uint8_t uDecodeMode3Bit)
 {
    (void)f;
-   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+   if(uDecodeMode3Bit>= QCBOR_DECODE_MODE_PREFERRED) {
       return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE;
    } else {
       return QCBOR_SUCCESS;
@@ -1262,10 +1267,10 @@
 }
 
 static QCBORError
-QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode)
+QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode3Bit)
 {
    (void)d;
-   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+   if(uDecodeMode3Bit>= QCBOR_DECODE_MODE_PREFERRED) {
       return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE;
    } else {
       return QCBOR_SUCCESS;
@@ -1278,7 +1283,7 @@
  * Decode a float
  */
 static QCBORError
-QCBOR_Private_DecodeFloat(const uint8_t  uDecodeMode,
+QCBOR_Private_DecodeFloat(const uint8_t  uDecodeMode3Bit,
                           const int      nAdditionalInfo,
                           const uint64_t uArgument,
                           QCBORItem     *pDecodedItem)
@@ -1298,7 +1303,7 @@
          pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument);
          pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
 
-         uReturn = QCBORDecode_Private_HalfConformance(pDecodedItem->val.dfnum, uDecodeMode);
+         uReturn = QCBORDecode_Private_HalfConformance(pDecodedItem->val.dfnum, uDecodeMode3Bit);
          if(uReturn != QCBOR_SUCCESS) {
             break;
          }
@@ -1316,7 +1321,7 @@
           * 32 bits. It was widened to 64 bits to be passed in here.
           */
          single = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument);
-         uReturn = QCBORDecode_Private_SingleConformance(single, uDecodeMode);
+         uReturn = QCBORDecode_Private_SingleConformance(single, uDecodeMode3Bit);
          if(uReturn != QCBOR_SUCCESS) {
             break;
          }
@@ -1344,7 +1349,7 @@
          pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument);
          pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
 
-         uReturn = QCBORDecode_Private_DoubleConformance(pDecodedItem->val.dfnum, uDecodeMode);
+         uReturn = QCBORDecode_Private_DoubleConformance(pDecodedItem->val.dfnum, uDecodeMode3Bit);
          if(uReturn != QCBOR_SUCCESS) {
             break;
          }
@@ -1402,7 +1407,7 @@
  *                                           type in input.
  */
 static QCBORError
-QCBOR_Private_DecodeType7(const uint8_t  uDecodeMode,
+QCBOR_Private_DecodeType7(const uint8_t  uDecodeMode3Bit,
                           const int      nAdditionalInfo,
                           const uint64_t uArgument,
                           QCBORItem     *pDecodedItem)
@@ -1425,7 +1430,7 @@
       case SINGLE_PREC_FLOAT: /* 26 */
       case DOUBLE_PREC_FLOAT: /* 27 */
 #ifndef USEFULBUF_DISABLE_ALL_FLOAT
-         uReturn = QCBOR_Private_DecodeFloat(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
+         uReturn = QCBOR_Private_DecodeFloat(uDecodeMode3Bit, nAdditionalInfo, uArgument, pDecodedItem);
 #else
          uReturn = QCBOR_ERR_ALL_FLOAT_DISABLED;
 #endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
@@ -1437,7 +1442,7 @@
       case CBOR_SIMPLEV_UNDEF: /* 23 */
       case CBOR_SIMPLE_BREAK:  /* 31 */
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
-         if(uDecodeMode >= QCBOR_ENCODE_MODE_DCBOR &&
+         if(uDecodeMode3Bit >= QCBOR_ENCODE_MODE_DCBOR &&
             nAdditionalInfo == CBOR_SIMPLEV_UNDEF) {
             uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
             goto Done;
@@ -1457,7 +1462,7 @@
 
       default: /* 0-19 */
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
-         if(uDecodeMode >= QCBOR_ENCODE_MODE_DCBOR &&
+         if(uDecodeMode3Bit >= QCBOR_ENCODE_MODE_DCBOR &&
             (uArgument < CBOR_SIMPLEV_FALSE || uArgument > CBOR_SIMPLEV_NULL)) {
             uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
             goto Done;
@@ -1518,6 +1523,7 @@
    int       nMajorType = 0;
    uint64_t  uArgument = 0;
    int       nAdditionalInfo = 0;
+   uint8_t   uDecodeMode3Bit = pMe->uDecodeMode & QCBOR_DECODE_MODE_MASK;
 
    memset(pDecodedItem, 0, sizeof(QCBORItem));
 
@@ -1527,7 +1533,7 @@
    uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf),
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
                                       // TODO: make this prettier; will optimizer take out stuff without ifdef?
-                                      pMe->uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED,
+                                      uDecodeMode3Bit >= QCBOR_DECODE_MODE_PREFERRED,
 #endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */
                                       &nMajorType,
                                       &uArgument,
@@ -1554,16 +1560,16 @@
 
       case CBOR_MAJOR_TYPE_ARRAY: /* Major type 4 */
       case CBOR_MAJOR_TYPE_MAP:   /* Major type 5 */
-         return QCBOR_Private_DecodeArrayOrMap(pMe->uDecodeMode, nMajorType, uArgument, nAdditionalInfo, pDecodedItem);
+         return QCBOR_Private_DecodeArrayOrMap(uDecodeMode3Bit, nMajorType, uArgument, nAdditionalInfo, pDecodedItem);
          break;
 
       case CBOR_MAJOR_TYPE_TAG: /* Major type 6, tag numbers */
-         return QCBOR_Private_DecodeTag(uArgument, nAdditionalInfo, pDecodedItem);
+         return QCBOR_Private_DecodeTagNumber(uArgument, nAdditionalInfo, pDecodedItem);
          break;
 
       case CBOR_MAJOR_TYPE_SIMPLE:
          /* Major type 7: float, double, true, false, null... */
-         return QCBOR_Private_DecodeType7(pMe->uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
+         return QCBOR_Private_DecodeType7(uDecodeMode3Bit, nAdditionalInfo, uArgument, pDecodedItem);
          break;
 
       default:
@@ -1742,9 +1748,9 @@
 /**
  * @brief This converts a tag number to a shorter mapped value for storage.
  *
- * @param[in] pMe                The decode context.
- * @param[in] uUnMappedTag       The tag number to map
- * @param[out] puMappedTagNumer  The stored tag number.
+ * @param[in] pMe                 The decode context.
+ * @param[in] uUnMappedTag        The tag number to map
+ * @param[out] puMappedTagNumber  The stored tag number.
  *
  * @return error code.
  *
@@ -1760,7 +1766,7 @@
 static QCBORError
 QCBORDecode_Private_MapTagNumber(QCBORDecodeContext *pMe,
                                  const uint64_t      uUnMappedTag,
-                                 uint16_t           *puMappedTagNumer)
+                                 uint16_t           *puMappedTagNumber)
 {
    if(uUnMappedTag > QCBOR_LAST_UNMAPPED_TAG) {
       unsigned uTagMapIndex;
@@ -1779,10 +1785,10 @@
 
       /* Covers the cases where tag is new and were it is already in the map */
       pMe->auMappedTags[uTagMapIndex] = uUnMappedTag;
-      *puMappedTagNumer = (uint16_t)(uTagMapIndex + QCBOR_LAST_UNMAPPED_TAG + 1);
+      *puMappedTagNumber = (uint16_t)(uTagMapIndex + QCBOR_LAST_UNMAPPED_TAG + 1);
 
    } else {
-      *puMappedTagNumer = (uint16_t)uUnMappedTag;
+      *puMappedTagNumber = (uint16_t)uUnMappedTag;
    }
 
    return QCBOR_SUCCESS;
@@ -1816,7 +1822,31 @@
       return pMe->auMappedTags[uIndex];
    }
 }
-#endif /* QCBOR_DISABLE_TAGS */
+
+
+static const struct QCBORTagDecoderEntry *
+QCBORDecode_Private_LookUpTagDecoder(const struct QCBORTagDecoderEntry *pTagContentTable,
+                                     const uint64_t                     uTagNumber)
+{
+   const struct QCBORTagDecoderEntry *pTE;
+
+   if(pTagContentTable == NULL) {
+      return NULL;
+   }
+
+   for(pTE = pTagContentTable; pTE->uTagNumber != CBOR_TAG_INVALID64; pTE++) {
+      if(pTE->uTagNumber == uTagNumber || pTE->uTagNumber == CBOR_TAG_ANY) {
+         break;
+      }
+   }
+
+   if(pTE->uTagNumber == CBOR_TAG_INVALID64) {
+      return NULL;
+   }
+
+   return pTE;
+}
+#endif /* ! QCBOR_DISABLE_TAGS */
 
 
 /**
@@ -1857,70 +1887,68 @@
                                      QCBORItem          *pDecodedItem)
 {
 #ifndef QCBOR_DISABLE_TAGS
-   /* Accummulate the tags from multiple items here and then copy them
-    * into the last item, the non-tag item.
+   int         nIndex;
+   QCBORError  uErr;
+   uint16_t    uMappedTagNumber;
+   QCBORError  uReturn;
+
+   /* Accummulate the tag numbers from multiple items here and then
+    * copy them into the last item, the non-tag-number item.
     */
-   uint16_t auItemsTags[QCBOR_MAX_TAGS_PER_ITEM];
+   QCBORMappedTagNumbers  auTagNumbers;;
 
    /* Initialize to CBOR_TAG_INVALID16 */
    #if CBOR_TAG_INVALID16 != 0xffff
-   /* Be sure the memset does the right thing. */
+   /* Be sure the memset is initializing to CBOR_TAG_INVALID16 */
    #err CBOR_TAG_INVALID16 tag not defined as expected
    #endif
-   memset(auItemsTags, 0xff, sizeof(auItemsTags));
+   memset(auTagNumbers, 0xff, sizeof(auTagNumbers));
 
-   QCBORError uReturn = QCBOR_SUCCESS;
-
-   /* Loop fetching data items until the item fetched is not a tag */
-   for(;;) {
-      QCBORError uErr = QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem);
+   /* Loop fetching data items until the item fetched is not a tag number */
+   uReturn = QCBOR_SUCCESS;
+   for(nIndex = 0; ; nIndex++) {
+      uErr = QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem);
       if(uErr != QCBOR_SUCCESS) {
          uReturn = uErr;
-         goto Done;
-      }
-
-      if(pDecodedItem->uDataType != QCBOR_TYPE_TAG) {
-         /* Successful exit from loop; maybe got some tags, maybe not */
-         memcpy(pDecodedItem->uTags, auItemsTags, sizeof(auItemsTags));
          break;
       }
 
-      if(auItemsTags[QCBOR_MAX_TAGS_PER_ITEM - 1] != CBOR_TAG_INVALID16) {
-         /* No room in the tag list */
+      if(pDecodedItem->uDataType != QCBOR_TYPE_TAG_NUMBER) {
+         /* Successful exit from loop; maybe got some tags, maybe not */
+         memcpy(pDecodedItem->auTagNumbers, auTagNumbers, sizeof(auTagNumbers));
+         break;
+      }
+
+      if(nIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
+         /* No room in the item's tag number array */
          uReturn = QCBOR_ERR_TOO_MANY_TAGS;
-         /* Continue on to get all tags wrapping this item even though
-          * it is erroring out in the end. This allows decoding to
-          * continue. This is a resource limit error, not a problem
-          * with being well-formed CBOR.
+         /* Continue on to get all tag numbers wrapping this item even
+          * though it is erroring out in the end. This allows decoding
+          * to continue. This is a QCBOR resource limit error, not a
+          * problem with being well-formed CBOR.
           */
          continue;
       }
-      /* Slide tags over one in the array to make room at index 0.
-       * Must use memmove because the move source and destination
-       * overlap.
-       */
-      memmove(&auItemsTags[1],
-              auItemsTags,
-              sizeof(auItemsTags) - sizeof(auItemsTags[0]));
 
-      /* Map the tag */
-      uint16_t uMappedTagNumber = 0;
-      uReturn = QCBORDecode_Private_MapTagNumber(pMe, pDecodedItem->val.uTagV, &uMappedTagNumber);
-      /* Continue even on error so as to consume all tags wrapping
-       * this data item so decoding can go on. If MapTagNumber()
-       * errors once it will continue to error.
+      /* Map the tag number */
+      uMappedTagNumber = 0;
+      uReturn = QCBORDecode_Private_MapTagNumber(pMe, pDecodedItem->val.uTagNumber, &uMappedTagNumber);
+      /* Continue even on error so as to consume all tag numbers
+       * wrapping this data item so decoding can go on. If
+       * QCBORDecode_Private_MapTagNumber() errors once it will
+       * continue to error.
        */
-      auItemsTags[0] = uMappedTagNumber;
+
+      auTagNumbers[nIndex] = uMappedTagNumber;
    }
 
-Done:
    return uReturn;
 
-#else /* QCBOR_DISABLE_TAGS */
+#else /* ! QCBOR_DISABLE_TAGS */
 
    return QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem);
 
-#endif /* QCBOR_DISABLE_TAGS */
+#endif /* ! QCBOR_DISABLE_TAGS */
 }
 
 
@@ -1987,7 +2015,7 @@
 
    /* Decoding a map entry, so the item decoded above was the label */
    LabelItem = *pDecodedItem;
-   
+
    if(puLabelEndOffset != NULL) {
        /* Cast is OK because lengths are all 32-bit in QCBOR */
        *puLabelEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
@@ -2000,7 +2028,8 @@
       goto Done;
    }
    if(uErr2 != QCBOR_SUCCESS) {
-      /* The recoverable error for the value overrides the recoverable error for the label, if there was an error for the label */
+      /* The recoverable error for the value overrides the recoverable
+       * error for the label, if there was an error for the label */
       uErr = uErr2;
    }
 
@@ -2009,10 +2038,10 @@
    pDecodedItem->uLabelType  = LabelItem.uDataType;
 
 #ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
-   /* QCBOR_DECODE_MODE_MAP_STRINGS_ONLY might have been a bad idea. Maybe
+   /* TODO: 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 &&
+   if((pMe->uDecodeMode & QCBOR_DECODE_MODE_MASK) == QCBOR_DECODE_MODE_MAP_STRINGS_ONLY &&
       LabelItem.uDataType != QCBOR_TYPE_TEXT_STRING) {
       uErr = QCBOR_ERR_MAP_LABEL_TYPE;
       goto Done;
@@ -2352,478 +2381,6 @@
 }
 
 
-#ifndef QCBOR_DISABLE_TAGS
-/**
- * @brief Shift 0th tag out of the tag list.
- *
- * pDecodedItem[in,out]  The data item to convert.
- *
- * The 0th tag is discarded. \ref CBOR_TAG_INVALID16 is
- * shifted into empty slot at the end of the tag list.
- */
-static void
-QCBOR_Private_ShiftTags(QCBORItem *pDecodedItem)
-{
-   for(int i = 0; i < QCBOR_MAX_TAGS_PER_ITEM-1; i++) {
-      pDecodedItem->uTags[i] = pDecodedItem->uTags[i+1];
-   }
-   pDecodedItem->uTags[QCBOR_MAX_TAGS_PER_ITEM-1] = CBOR_TAG_INVALID16;
-}
-#endif /* QCBOR_DISABLE_TAGS */
-
-
-/**
- * @brief Convert different epoch date formats in to the QCBOR epoch date format
- *
- * pDecodedItem[in,out]  The data item to convert.
- *
- * @retval QCBOR_ERR_DATE_OVERFLOW              65-bit negative integer.
- * @retval QCBOR_ERR_FLOAT_DATE_DISABLED        Float-point date in input,
- *                                              floating-point date disabled.
- * @retval QCBOR_ERR_ALL_FLOAT_DISABLED         Float-point date in input,
- *                                              all floating-point disabled.
- * @retval QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT  Unexpected and unrecoverable
- *                                              error decoding date.
- *
- * The epoch date tag defined in QCBOR allows for floating-point
- * dates. It even allows a protocol to flop between date formats when
- * ever it wants.  Floating-point dates aren't that useful as they are
- * only needed for dates beyond the age of the earth.
- *
- * This converts all the date formats into one format of an unsigned
- * integer plus a floating-point fraction.
- */
-static QCBORError
-QCBOR_Private_DecodeDateEpoch(QCBORItem *pDecodedItem)
-{
-   QCBORError uReturn = QCBOR_SUCCESS;
-
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-   pDecodedItem->val.epochDate.fSecondsFraction = 0;
-#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
-
-   switch (pDecodedItem->uDataType) {
-
-      case QCBOR_TYPE_INT64:
-         pDecodedItem->val.epochDate.nSeconds = pDecodedItem->val.int64;
-         break;
-
-      case QCBOR_TYPE_UINT64:
-         /* This only happens for CBOR type 0 > INT64_MAX so it is
-          * always an overflow.
-          */
-         uReturn = QCBOR_ERR_DATE_OVERFLOW;
-         goto Done;
-         break;
-
-      case QCBOR_TYPE_DOUBLE:
-      case QCBOR_TYPE_FLOAT:
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
-      {
-         /* Convert working value to double if input was a float */
-         const double d = pDecodedItem->uDataType == QCBOR_TYPE_DOUBLE ?
-                   pDecodedItem->val.dfnum :
-                   (double)pDecodedItem->val.fnum;
-
-         /* The conversion from float to integer requires overflow
-          * detection since floats can be much larger than integers.
-          * This implementation errors out on these large float values
-          * since they are beyond the age of the earth.
-          *
-          * These constants for the overflow check are computed by the
-          * compiler. They are not computed at run time.
-          *
-          * The factor of 0x7ff is added/subtracted to avoid a
-          * rounding error in the wrong direction when the compiler
-          * computes these constants. There is rounding because a
-          * 64-bit integer has 63 bits of precision where a double
-          * only has 53 bits. Without the 0x7ff factor, the compiler
-          * may round up and produce a double for the bounds check
-          * that is larger than can be stored in a 64-bit integer. The
-          * amount of 0x7ff is picked because it has 11 bits set.
-          *
-          * Without the 0x7ff there is a ~30 minute range of time
-          * values 10 billion years in the past and in the future
-          * where this code could go wrong. Some compilers
-          * generate a warning or error without the 0x7ff.
-          */
-         const double dDateMax = (double)(INT64_MAX - 0x7ff);
-         const double dDateMin = (double)(INT64_MIN + 0x7ff);
-
-         if(isnan(d) || d > dDateMax || d < dDateMin) {
-            uReturn = QCBOR_ERR_DATE_OVERFLOW;
-            goto Done;
-         }
-
-         /* The actual conversion */
-         pDecodedItem->val.epochDate.nSeconds = (int64_t)d;
-         pDecodedItem->val.epochDate.fSecondsFraction =
-                           d - (double)pDecodedItem->val.epochDate.nSeconds;
-      }
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
-
-         uReturn = QCBOR_ERR_HW_FLOAT_DISABLED;
-         goto Done;
-
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
-         break;
-
-      default:
-         /* It's the arrays and maps that are unrecoverable because
-          * they are not consumed here. Since this is just an error
-          * condition, no extra code is added here to make the error
-          * recoverable for non-arrays and maps like strings. */
-         uReturn = QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
-         goto Done;
-   }
-
-   pDecodedItem->uDataType = QCBOR_TYPE_DATE_EPOCH;
-
-Done:
-   return uReturn;
-}
-
-
-/**
- * @brief Convert the days epoch date.
- *
- * pDecodedItem[in,out]  The data item to convert.
- *
- * @retval QCBOR_ERR_DATE_OVERFLOW              65-bit negative integer.
- * @retval QCBOR_ERR_FLOAT_DATE_DISABLED        Float-point date in input,
- *                                              floating-point date disabled.
- * @retval QCBOR_ERR_ALL_FLOAT_DISABLED         Float-point date in input,
- *                                              all floating-point disabled.
- * @retval QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT  Unexpected and unrecoverable
- *                                              error decoding date.
- *
- * This is much simpler than the other epoch date format because
- * floating-porint is not allowed. This is mostly a simple type check.
- */
-static QCBORError
-QCBOR_Private_DecodeDaysEpoch(QCBORItem *pDecodedItem)
-{
-   QCBORError uReturn = QCBOR_SUCCESS;
-
-   switch (pDecodedItem->uDataType) {
-
-      case QCBOR_TYPE_INT64:
-         pDecodedItem->val.epochDays = pDecodedItem->val.int64;
-         break;
-
-      case QCBOR_TYPE_UINT64:
-         /* This only happens for CBOR type 0 > INT64_MAX so it is
-          * always an overflow.
-          */
-         uReturn = QCBOR_ERR_DATE_OVERFLOW;
-         goto Done;
-         break;
-
-      default:
-         /* It's the arrays and maps that are unrecoverable because
-          * they are not consumed here. Since this is just an error
-          * condition, no extra code is added here to make the error
-          * recoverable for non-arrays and maps like strings. */
-         uReturn = QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
-         goto Done;
-         break;
-   }
-
-   pDecodedItem->uDataType = QCBOR_TYPE_DAYS_EPOCH;
-
-Done:
-   return uReturn;
-}
-
-
-#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
-
-/* Forward declaration is necessary for
- * QCBORDecode_MantissaAndExponent().  to be able to decode bignum
- * tags in the mantissa. If the mantissa is a decimal fraction or big
- * float in error, this will result in a recurive call to
- * QCBORDecode_MantissaAndExponent(), but the recursion will unwined
- * correctly and the correct error is returned.
- */
-static QCBORError
-QCBORDecode_Private_GetNextTagContent(QCBORDecodeContext *pMe,
-                                      QCBORItem          *pDecodedItem);
-
-
-/**
- * @brief Decode decimal fractions and big floats.
- *
- * @param[in] pMe               The decode context.
- * @param[in,out] pDecodedItem  On input the array data item that
- *                              holds the mantissa and exponent.  On
- *                              output the decoded mantissa and
- *                              exponent.
- *
- * @returns  Decoding errors from getting primitive data items or
- *           \ref QCBOR_ERR_BAD_EXP_AND_MANTISSA.
- *
- * When called pDecodedItem must be the array with two members, the
- * exponent and mantissa.
- *
- * This will fetch and decode the exponent and mantissa and put the
- * result back into pDecodedItem.
- *
- * This does no checking or processing of tag numbers. That is to be
- * done by the code that calls this.
- *
- * This stuffs the type of the mantissa into pDecodedItem with the expectation
- * the caller will process it.
- */
-static QCBORError
-QCBORDecode_Private_ExpMantissa(QCBORDecodeContext *pMe,
-                                QCBORItem          *pDecodedItem)
-{
-   QCBORError uReturn;
-
-   /* --- Make sure it is an array; track nesting level of members --- */
-   if(pDecodedItem->uDataType != QCBOR_TYPE_ARRAY) {
-      uReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
-      goto Done;
-   }
-
-   /* A check for pDecodedItem->val.uCount == 2 would work for
-    * definite-length arrays, but not for indefinite. Instead remember
-    * the nesting level the two integers must be at, which is one
-    * deeper than that of the array.
-    */
-   const int nNestLevel = pDecodedItem->uNestingLevel + 1;
-
-   /* --- Get the exponent --- */
-   QCBORItem exponentItem;
-   uReturn = QCBORDecode_GetNext(pMe, &exponentItem);
-   if(uReturn != QCBOR_SUCCESS) {
-      goto Done;
-   }
-   if(exponentItem.uNestingLevel != nNestLevel) {
-      /* Array is empty or a map/array encountered when expecting an int */
-      uReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
-      goto Done;
-   }
-   if(exponentItem.uDataType == QCBOR_TYPE_INT64) {
-     /* Data arriving as an unsigned int < INT64_MAX has been
-      * converted to QCBOR_TYPE_INT64 and thus handled here. This is
-      * also means that the only data arriving here of type
-      * QCBOR_TYPE_UINT64 data will be too large for this to handle
-      * and thus an error that will get handled in the next else.
-      */
-     pDecodedItem->val.expAndMantissa.nExponent = exponentItem.val.int64;
-   } else {
-      /* Wrong type of exponent or a QCBOR_TYPE_UINT64 > INT64_MAX */
-      uReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
-      goto Done;
-   }
-
-   /* --- Get the mantissa --- */
-   QCBORItem mantissaItem;
-   uReturn = QCBORDecode_GetNext(pMe, &mantissaItem);
-   if(uReturn != QCBOR_SUCCESS) {
-      goto Done;
-   }
-   if(mantissaItem.uNestingLevel != nNestLevel) {
-      /* Mantissa missing or map/array encountered when expecting number */
-      uReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
-      goto Done;
-   }
-   /* Stuff the mantissa data type into the item to send it up to the
-    * the next level. */
-   pDecodedItem->uDataType = mantissaItem.uDataType;
-   if(mantissaItem.uDataType == QCBOR_TYPE_INT64) {
-      /* Data arriving as an unsigned int < INT64_MAX has been
-       * converted to QCBOR_TYPE_INT64 and thus handled here. This is
-       * also means that the only data arriving here of type
-       * QCBOR_TYPE_UINT64 data will be too large for this to handle
-       * and thus an error that will get handled in an else below.
-       */
-      pDecodedItem->val.expAndMantissa.Mantissa.nInt = mantissaItem.val.int64;
-#ifndef QCBOR_DISABLE_TAGS
-      /* With tags fully disabled a big number mantissa will error out
-       * in the call to QCBORDecode_GetNextWithTags() because it has
-       * a tag number.
-       */
-   }  else if(mantissaItem.uDataType == QCBOR_TYPE_POSBIGNUM ||
-              mantissaItem.uDataType == QCBOR_TYPE_NEGBIGNUM) {
-      /* Got a good big num mantissa */
-      pDecodedItem->val.expAndMantissa.Mantissa.bigNum = mantissaItem.val.bigNum;
-#endif /* QCBOR_DISABLE_TAGS */
-   } else {
-      /* Wrong type of mantissa or a QCBOR_TYPE_UINT64 > INT64_MAX */
-      uReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
-      goto Done;
-   }
-
-   /* --- Check that array only has the two numbers --- */
-   if(mantissaItem.uNextNestLevel == nNestLevel) {
-      /* Extra items in the decimal fraction / big float */
-      /* Improvement: this should probably be an unrecoverable error. */
-      uReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
-      goto Done;
-   }
-   pDecodedItem->uNextNestLevel = mantissaItem.uNextNestLevel;
-
-Done:
-  return uReturn;
-}
-#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
-
-
-#ifndef QCBOR_DISABLE_TAGS
-
-#ifndef QCBOR_DISABLE_UNCOMMON_TAGS
-/**
- * @brief Decode the MIME type tag
- *
- * @param[in,out] pDecodedItem   The item to decode.
- *
- *  Handle the text and binary MIME type tags. Slightly too complicated
- *  f or ProcessTaggedString() because the RFC 7049 MIME type was
- *  incorreclty text-only.
- */
-static QCBORError
-QCBOR_Private_DecodeMIME(QCBORItem *pDecodedItem)
-{
-   if(pDecodedItem->uDataType == QCBOR_TYPE_TEXT_STRING) {
-      pDecodedItem->uDataType = QCBOR_TYPE_MIME;
-   } else if(pDecodedItem->uDataType == QCBOR_TYPE_BYTE_STRING) {
-      pDecodedItem->uDataType = QCBOR_TYPE_BINARY_MIME;
-   } else {
-      /* It's the arrays and maps that are unrecoverable because
-       * they are not consumed here. Since this is just an error
-       * condition, no extra code is added here to make the error
-       * recoverable for non-arrays and maps like strings. */
-      return QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
-   }
-
-   return QCBOR_SUCCESS;
-}
-#endif /* QCBOR_DISABLE_UNCOMMON_TAGS */
-
-/**
- * Table of CBOR tags whose content is either a text string or a byte
- * string. The table maps the CBOR tag to the QCBOR type. The high-bit
- * of uQCBORtype indicates the content should be a byte string rather
- * than a text string
- */
-struct StringTagMapEntry {
-   uint16_t uTagNumber;
-   uint8_t  uQCBORtype;
-};
-
-#define IS_BYTE_STRING_BIT 0x80
-#define QCBOR_TYPE_MASK   ~IS_BYTE_STRING_BIT
-
-static const struct StringTagMapEntry QCBOR_Private_StringTagMap[] = {
-   {CBOR_TAG_DATE_STRING,   QCBOR_TYPE_DATE_STRING},
-   {CBOR_TAG_DAYS_STRING,   QCBOR_TYPE_DAYS_STRING},
-   {CBOR_TAG_POS_BIGNUM,    QCBOR_TYPE_POSBIGNUM | IS_BYTE_STRING_BIT},
-   {CBOR_TAG_NEG_BIGNUM,    QCBOR_TYPE_NEGBIGNUM | IS_BYTE_STRING_BIT},
-   {CBOR_TAG_CBOR,          QBCOR_TYPE_WRAPPED_CBOR | IS_BYTE_STRING_BIT},
-   {CBOR_TAG_URI,           QCBOR_TYPE_URI},
-#ifndef QCBOR_DISABLE_UNCOMMON_TAGS
-   {CBOR_TAG_B64URL,        QCBOR_TYPE_BASE64URL},
-   {CBOR_TAG_B64,           QCBOR_TYPE_BASE64},
-   {CBOR_TAG_REGEX,         QCBOR_TYPE_REGEX},
-   {CBOR_TAG_BIN_UUID,      QCBOR_TYPE_UUID | IS_BYTE_STRING_BIT},
-#endif /* QCBOR_DISABLE_UNCOMMON_TAGS */
-   {CBOR_TAG_CBOR_SEQUENCE, QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE | IS_BYTE_STRING_BIT},
-   {CBOR_TAG_INVALID16,     QCBOR_TYPE_NONE}
-};
-
-
-/**
- * @brief Process standard CBOR tags whose content is a string
- *
- * @param[in] uTag              The tag.
- * @param[in,out] pDecodedItem  The data item.
- *
- * @returns  This returns QCBOR_SUCCESS if the tag was procssed,
- *           \ref QCBOR_ERR_UNSUPPORTED if the tag was not processed and
- *           \ref QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT if the content type was wrong for the tag.
- *
- * Process the CBOR tags that whose content is a byte string or a text
- * string and for which the string is just passed on to the caller.
- *
- * This maps the CBOR tag to the QCBOR type and checks the content
- * type.  Nothing more. It may not be the most important
- * functionality, but it part of implementing as much of RFC 8949 as
- * possible.
- */
-static QCBORError
-QCBOR_Private_ProcessTaggedString(uint16_t uTag, QCBORItem *pDecodedItem)
-{
-   /* This only works on tags that were not mapped; no need for other yet */
-   if(uTag > QCBOR_LAST_UNMAPPED_TAG) {
-      return QCBOR_ERR_UNSUPPORTED;
-   }
-
-   unsigned uIndex;
-   for(uIndex = 0; QCBOR_Private_StringTagMap[uIndex].uTagNumber != CBOR_TAG_INVALID16; uIndex++) {
-      if(QCBOR_Private_StringTagMap[uIndex].uTagNumber == uTag) {
-         break;
-      }
-   }
-
-   const uint8_t uQCBORType = QCBOR_Private_StringTagMap[uIndex].uQCBORtype;
-   if(uQCBORType == QCBOR_TYPE_NONE) {
-      /* repurpose this error to mean not handled here */
-      return QCBOR_ERR_UNSUPPORTED;
-   }
-
-   uint8_t uExpectedType = QCBOR_TYPE_TEXT_STRING;
-   if(uQCBORType & IS_BYTE_STRING_BIT) {
-      uExpectedType = QCBOR_TYPE_BYTE_STRING;
-   }
-
-   if(pDecodedItem->uDataType != uExpectedType) {
-      /* It's the arrays and maps that are unrecoverable because
-       * they are not consumed here. Since this is just an error
-       * condition, no extra code is added here to make the error
-       * recoverable for non-arrays and maps like strings. */
-      return QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
-   }
-
-   pDecodedItem->uDataType = (uint8_t)(uQCBORType & QCBOR_TYPE_MASK);
-   return QCBOR_SUCCESS;
-}
-#endif /* QCBOR_DISABLE_TAGS */
-
-
-#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
-/**
- * @brief Figures out data type for exponent mantissa tags.
- *
- * @param[in] uTagToProcess  Either @ref CBOR_TAG_DECIMAL_FRACTION or
- *                           @ref CBOR_TAG_BIG_FLOAT.
- * @param[in] pDecodedItem   Item being decoded.
- *
- * @returns One of the 6 values between \ref QCBOR_TYPE_DECIMAL_FRACTION
- *          and @ref QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM.
- *
- * Does mapping between a CBOR tag number and a QCBOR type.  with a
- * little bit of logic and arithmatic.
- *
- * Used in serveral contexts. Does the work where sometimes the data
- * item is explicitly tagged and sometimes not.
- */
-static uint8_t
-QCBOR_Private_ExpMantissaDataType(const uint16_t   uTagToProcess,
-                                  const QCBORItem *pDecodedItem)
-{
-   uint8_t uBase = uTagToProcess == CBOR_TAG_DECIMAL_FRACTION ?
-                                       QCBOR_TYPE_DECIMAL_FRACTION :
-                                       QCBOR_TYPE_BIGFLOAT;
-   if(pDecodedItem->uDataType != QCBOR_TYPE_INT64) {
-      uBase = (uint8_t)(uBase + pDecodedItem->uDataType - QCBOR_TYPE_POSBIGNUM + 1);
-   }
-   return uBase;
-}
-#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
-
-
 /**
  * @brief Decode tag content for select tags (decoding layer 1).
  *
@@ -2834,88 +2391,54 @@
  *
  * CBOR tag numbers for the item were decoded in GetNext_TaggedItem(),
  * but the whole tag was not decoded. Here, the whole tags (tag number
- * and tag content) that are supported by QCBOR are decoded. This is a
+ * and tag content) are decoded. This is a
  * quick pass through for items that are not tags.
  */
 static QCBORError
 QCBORDecode_Private_GetNextTagContent(QCBORDecodeContext *pMe,
                                       QCBORItem          *pDecodedItem)
 {
-   QCBORError uReturn;
+   QCBORError uErr;
 
-   uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem, NULL);
-   if(uReturn != QCBOR_SUCCESS) {
+   uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem, NULL);
+
+#ifndef QCBOR_DISABLE_TAGS
+   uint64_t   uTagNumber;
+   int        nTagIndex;
+   const struct QCBORTagDecoderEntry *pTagDecoder;
+
+   if(uErr != QCBOR_SUCCESS) {
       goto Done;
    }
 
-#ifndef QCBOR_DISABLE_TAGS
-   /* When there are no tag numbers for the item, this exits first
-    * thing and effectively does nothing.
-    *
-    * This loops over all the tag numbers accumulated for this item
-    * trying to decode and interpret them. This stops at the end of
-    * the list or at the first tag number that can't be interpreted by
-    * this code. This is effectively a recursive processing of the
-    * tags number list that handles nested tags.
-    */
-   while(1) {
-      /* Don't bother to unmap tags via QCBORITem.uTags since this
-       * code only works on tags less than QCBOR_LAST_UNMAPPED_TAG.
-       */
-      const uint16_t uTagToProcess = pDecodedItem->uTags[0];
+   /* Loop over tag numbers in reverse, those closest to content first */
+   for(nTagIndex = QCBOR_MAX_TAGS_PER_ITEM-1; nTagIndex >= 0; nTagIndex--) {
 
-      if(uTagToProcess == CBOR_TAG_INVALID16) {
-         /* Hit the end of the tag list. A successful exit. */
-         break;
-
-      } else if(uTagToProcess == CBOR_TAG_DATE_EPOCH) {
-         uReturn = QCBOR_Private_DecodeDateEpoch(pDecodedItem);
-
-      } else if(uTagToProcess == CBOR_TAG_DAYS_EPOCH) {
-         uReturn = QCBOR_Private_DecodeDaysEpoch(pDecodedItem);
-
-#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
-      } else if(uTagToProcess == CBOR_TAG_DECIMAL_FRACTION ||
-                uTagToProcess == CBOR_TAG_BIGFLOAT) {
-         uReturn = QCBORDecode_Private_ExpMantissa(pMe, pDecodedItem);
-         /* --- Which is it, decimal fraction or a bigfloat? --- */
-         pDecodedItem->uDataType = QCBOR_Private_ExpMantissaDataType(uTagToProcess, pDecodedItem);
-
-#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
-#ifndef QCBOR_DISABLE_UNCOMMON_TAGS
-      } else if(uTagToProcess == CBOR_TAG_MIME ||
-                uTagToProcess == CBOR_TAG_BINARY_MIME) {
-         uReturn = QCBOR_Private_DecodeMIME(pDecodedItem);
-#endif /* QCBOR_DISABLE_UNCOMMON_TAGS */
-
-      } else {
-         /* See if it is a passthrough byte/text string tag; process if so */
-         uReturn = QCBOR_Private_ProcessTaggedString(pDecodedItem->uTags[0], pDecodedItem);
-
-         if(uReturn == QCBOR_ERR_UNSUPPORTED) {
-            /* It wasn't a passthrough byte/text string tag so it is
-             * an unknown tag. This is the exit from the loop on the
-             * first unknown tag.  It is a successful exit.
-             */
-            uReturn = QCBOR_SUCCESS;
-            break;
-         }
+      if(pDecodedItem->auTagNumbers[nTagIndex] == CBOR_TAG_INVALID16) {
+         continue; /* Empty slot, skip to next */
       }
 
-      if(uReturn != QCBOR_SUCCESS) {
-         /* Error exit from the loop */
-         break;
+      /* See if there's a content decoder for it */
+      uTagNumber  = QCBORDecode_Private_UnMapTagNumber(pMe, pDecodedItem->auTagNumbers[nTagIndex]);
+      pTagDecoder = QCBORDecode_Private_LookUpTagDecoder(pMe->pTagDecoderTable, uTagNumber);
+      if(pTagDecoder == NULL) {
+         break; /* Successful exist -- a tag that we can't decode */
       }
 
-      /* A tag was successfully processed, shift it out of the list of
-       * tags returned. This is the loop increment.
-       */
-      QCBOR_Private_ShiftTags(pDecodedItem);
+      /* Call the content decoder */
+      uErr = pTagDecoder->pfContentDecoder(pMe, pMe->pTagDecodersContext, pTagDecoder->uTagNumber, pDecodedItem);
+      if(uErr != QCBOR_SUCCESS) {
+         break; /* Error exit from the loop */
+      }
+
+      /* Remove tag number from list since its content was decoded */
+      pDecodedItem->auTagNumbers[nTagIndex] = CBOR_TAG_INVALID16;
    }
-#endif /* QCBOR_DISABLE_TAGS */
 
 Done:
-   return uReturn;
+#endif /* ! QCBOR_DISABLE_TAGS */
+
+   return uErr;
 }
 
 
@@ -2975,8 +2498,8 @@
 }
 
 
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
 /*
- *
  * This consumes the next item. It returns the starting position of
  * the label and the length of the label. It also returns the nest
  * level of the item consumed.
@@ -3134,6 +2657,44 @@
 
    return uErr;
 }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+
+static QCBORError
+QCBORDecode_Private_GetItemChecks(QCBORDecodeContext *pMe,
+                                  QCBORError          uErr,
+                                  const size_t        uOffset,
+                                  QCBORItem          *pDecodedItem)
+{
+   (void)pMe; /* Avoid warning for next two ifdefs */
+   (void)uOffset;
+
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+   if(uErr == QCBOR_SUCCESS &&
+      (pMe->uDecodeMode & QCBOR_DECODE_MODE_MASK) >= QCBOR_ENCODE_MODE_CDE &&
+      pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
+      /* Traverse map checking sort order and for duplicates */
+      uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem);
+   }
+#endif /* ! QCBOR_DISABLE_CONFORMANCE */
+
+#ifndef QCBOR_DISABLE_TAGS
+   if(uErr == QCBOR_SUCCESS &&
+      !(pMe->uDecodeMode & QCBOR_DECODE_UNPROCESSED_TAG_NUMBERS) &&
+      pDecodedItem->auTagNumbers[0] != CBOR_TAG_INVALID16) {
+      /*  Not QCBOR v1; there are tag numbers -- check they were consumed */
+      if(uOffset != pMe->uTagNumberCheckOffset || pMe->uTagNumberIndex != 255) {
+         uErr = QCBOR_ERR_UNPROCESSED_TAG_NUMBER;
+      }
+   }
+#endif /* ! QCBOR_DISABLE_TAGS */
+
+   if(uErr != QCBOR_SUCCESS) {
+      pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
+      pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
+   }
+
+   return uErr;
+}
 
 
 /*
@@ -3143,17 +2704,11 @@
 QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
 {
    QCBORError uErr;
+   size_t     uOffset;
+
+   uOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
    uErr = QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem);
-#ifndef QCBOR_DISABLE_CONFORMANCE
-   if(uErr == QCBOR_SUCCESS && pMe->uDecodeMode >= QCBOR_ENCODE_MODE_CDE && pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
-      /* Traverse map checking sort order and for duplicates */
-      uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem);
-   }
-#endif /* ! QCBOR_DISABLE_CONFORMANCE */
-   if(uErr != QCBOR_SUCCESS) {
-      pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
-      pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
-   }
+   uErr = QCBORDecode_Private_GetItemChecks(pMe, uErr, uOffset, pDecodedItem);
    return uErr;
 }
 
@@ -3193,10 +2748,10 @@
 
 
 static void
-QCBORDecode_Private_CopyTags(QCBORDecodeContext *pMe, const QCBORItem *pItem)
+QCBORDecode_Private_SaveTagNumbers(QCBORDecodeContext *pMe, const QCBORItem *pItem)
 {
 #ifndef QCBOR_DISABLE_TAGS
-   memcpy(pMe->uLastTags, pItem->uTags, sizeof(pItem->uTags));
+   memcpy(pMe->auLastTags, pItem->auTagNumbers, sizeof(pItem->auTagNumbers));
 #else
    (void)pMe;
    (void)pItem;
@@ -3216,77 +2771,7 @@
    }
 
    pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, pDecodedItem);
-   QCBORDecode_Private_CopyTags(pMe, pDecodedItem);
-}
-
-
-/*
- * Public function, see header qcbor/qcbor_decode.h file
- */
-QCBORError
-QCBORDecode_GetNextWithTags(QCBORDecodeContext *pMe,
-                            QCBORItem          *pDecodedItem,
-                            QCBORTagListOut    *pTags)
-{
-#ifndef QCBOR_DISABLE_TAGS
-
-   QCBORError uReturn;
-
-   uReturn = QCBORDecode_GetNext(pMe, pDecodedItem);
-   if(uReturn != QCBOR_SUCCESS) {
-      return uReturn;
-   }
-
-   if(pTags != NULL) {
-      pTags->uNumUsed = 0;
-      /* Reverse the order because pTags is reverse of QCBORItem.uTags. */
-      for(int nTagIndex = QCBOR_MAX_TAGS_PER_ITEM-1; nTagIndex >=0; nTagIndex--) {
-         if(pDecodedItem->uTags[nTagIndex] == CBOR_TAG_INVALID16) {
-            continue;
-         }
-         if(pTags->uNumUsed >= pTags->uNumAllocated) {
-            return QCBOR_ERR_TOO_MANY_TAGS;
-         }
-         pTags->puTags[pTags->uNumUsed] = QCBORDecode_Private_UnMapTagNumber(pMe,pDecodedItem->uTags[nTagIndex]);
-         pTags->uNumUsed++;
-      }
-   }
-
-   return QCBOR_SUCCESS;
-
-#else /* QCBOR_DISABLE_TAGS */
-   (void)pMe;
-   (void)pDecodedItem;
-   (void)pTags;
-   return QCBOR_ERR_TAGS_DISABLED;
-#endif /* QCBOR_DISABLE_TAGS */
-}
-
-
-/*
- * Public function, see header qcbor/qcbor_decode.h file
- */
-bool
-QCBORDecode_IsTagged(QCBORDecodeContext *pMe,
-                     const QCBORItem   *pItem,
-                     uint64_t           uTag)
-{
-#ifndef QCBOR_DISABLE_TAGS
-   for(unsigned uTagIndex = 0; uTagIndex < QCBOR_MAX_TAGS_PER_ITEM; uTagIndex++) {
-      if(pItem->uTags[uTagIndex] == CBOR_TAG_INVALID16) {
-         break;
-      }
-      if(QCBORDecode_Private_UnMapTagNumber(pMe, pItem->uTags[uTagIndex]) == uTag) {
-         return true;
-      }
-   }
-#else /* QCBOR_TAGS_DISABLED */
-   (void)pMe;
-   (void)pItem;
-   (void)uTag;
-#endif /* QCBOR_TAGS_DISABLED */
-
-   return false;
+   QCBORDecode_Private_SaveTagNumbers(pMe, pDecodedItem);
 }
 
 
@@ -3339,30 +2824,81 @@
 }
 
 
+#ifndef QCBOR_DISABLE_TAGS
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+uint64_t
+QCBORDecode_GetNthTagNumber(const QCBORDecodeContext *pMe,
+                            const QCBORItem          *pItem,
+                            uint8_t                   uIndex)
+{
+   if(pItem->uDataType == QCBOR_TYPE_NONE) {
+      return CBOR_TAG_INVALID64;
+   }
+   if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
+      return CBOR_TAG_INVALID64;
+   }
+
+   return QCBORDecode_Private_UnMapTagNumber(pMe, pItem->auTagNumbers[uIndex]);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+uint64_t
+QCBORDecode_GetNthTagNumberOfLast(QCBORDecodeContext *pMe,
+                                  uint8_t             uIndex)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return CBOR_TAG_INVALID64;
+   }
+   if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
+      return CBOR_TAG_INVALID64;
+   }
+
+   return QCBORDecode_Private_UnMapTagNumber(pMe, pMe->auLastTags[uIndex]);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+static uint64_t
+QCBORDecode_Private_GetNthTagNumberReverse(const QCBORDecodeContext *pMe,
+                                           const uint16_t            puTagNumbers[],
+                                           const uint32_t            uIndex)
+{
+   uint32_t uArrayIndex;
+
+   /* Find number of tag numbers */
+   for(uArrayIndex = QCBOR_MAX_TAGS_PER_ITEM-1; uArrayIndex > 0; uArrayIndex--) {
+      if(puTagNumbers[uArrayIndex] != CBOR_TAG_INVALID16) {
+         break;
+      }
+   }
+   if(uIndex > uArrayIndex) {
+      return CBOR_TAG_INVALID64;
+   }
+
+   return QCBORDecode_Private_UnMapTagNumber(pMe, puTagNumbers[uArrayIndex - uIndex]);
+}
+
+
 /*
  * Public function, see header qcbor/qcbor_decode.h file
  */
 uint64_t
 QCBORDecode_GetNthTag(QCBORDecodeContext *pMe,
                       const QCBORItem    *pItem,
-                      uint32_t            uIndex)
+                      const uint32_t      uIndex)
 {
-#ifndef QCBOR_DISABLE_TAGS
    if(pItem->uDataType == QCBOR_TYPE_NONE) {
       return CBOR_TAG_INVALID64;
    }
-   if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
-      return CBOR_TAG_INVALID64;
-   } else {
-      return QCBORDecode_Private_UnMapTagNumber(pMe, pItem->uTags[uIndex]);
-   }
-#else /* QCBOR_DISABLE_TAGS */
-   (void)pMe;
-   (void)pItem;
-   (void)uIndex;
 
-   return CBOR_TAG_INVALID64;
-#endif /* QCBOR_DISABLE_TAGS */
+   return QCBORDecode_Private_GetNthTagNumberReverse(pMe, pItem->auTagNumbers, uIndex);
 }
 
 
@@ -3373,26 +2909,67 @@
 QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pMe,
                             uint32_t                  uIndex)
 {
-#ifndef QCBOR_DISABLE_TAGS
-
    if(pMe->uLastError != QCBOR_SUCCESS) {
       return CBOR_TAG_INVALID64;
    }
    if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
       return CBOR_TAG_INVALID64;
-   } else {
-      return QCBORDecode_Private_UnMapTagNumber(pMe, pMe->uLastTags[uIndex]);
    }
-#else /* QCBOR_DISABLE_TAGS */
-   (void)pMe;
-   (void)uIndex;
 
-   return CBOR_TAG_INVALID64;
-#endif /* QCBOR_DISABLE_TAGS */
+   return QCBORDecode_Private_GetNthTagNumberReverse(pMe, pMe->auLastTags, uIndex);
 }
 
 
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+QCBORError
+QCBORDecode_GetNextTagNumber(QCBORDecodeContext *pMe, uint64_t *puTagNumber)
+{
+   QCBORItem   Item;
+   size_t      uOffset;
+   QCBORError  uErr;
 
+   const QCBORDecodeNesting SaveNesting = pMe->nesting;
+   const UsefulInputBuf Save = pMe->InBuf;
+
+   uOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+   if(uOffset == pMe->uTagNumberCheckOffset) {
+      pMe->uTagNumberIndex++;
+   } else {
+      pMe->uTagNumberIndex = 0;
+   }
+
+   *puTagNumber = CBOR_TAG_INVALID64;
+   uErr = QCBORDecode_Private_GetNextTagContent(pMe, &Item);
+   if(uErr) {
+      return uErr;
+   }
+
+   *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &Item, pMe->uTagNumberIndex);
+   if(*puTagNumber == CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTagNumber(pMe, &Item, pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) {
+      pMe->uTagNumberIndex = QCBOR_ALL_TAGS_PROCESSED;
+   }
+   pMe->uTagNumberCheckOffset = uOffset;
+
+   pMe->nesting = SaveNesting;
+   pMe->InBuf = Save;
+
+   return QCBOR_SUCCESS;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+void
+QCBORDecode_VGetNextTagNumber(QCBORDecodeContext *pMe, uint64_t *puTagNumber)
+{
+   pMe->uLastError = (uint8_t)QCBORDecode_GetNextTagNumber(pMe, puTagNumber);
+}
+
+#endif /* ! QCBOR_DISABLE_TAGS */
 
 #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
 
@@ -3912,38 +3489,152 @@
  * Public function, see header qcbor/qcbor_decode.h file
  */
 void
-QCBORDecode_GetItemInMapN(QCBORDecodeContext *pMe,
-                          int64_t             nLabel,
-                          uint8_t             uQcborType,
-                          QCBORItem          *pItem)
+QCBORDecode_SeekToLabelN(QCBORDecodeContext *pMe, int64_t nLabel)
 {
+   MapSearchInfo Info;
+   QCBORItem     OneItemSeach[2];
+
    if(pMe->uLastError != QCBOR_SUCCESS) {
       return;
    }
 
+   OneItemSeach[0].uLabelType  = QCBOR_TYPE_INT64;
+   OneItemSeach[0].label.int64 = nLabel;
+   OneItemSeach[0].uDataType   = QCBOR_TYPE_ANY;
+   OneItemSeach[1].uLabelType  = QCBOR_TYPE_NONE; // Indicates end of array
+
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL);
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset);
+   }
+}
+
+
+void
+QCBORDecode_SeekToLabelSZ(QCBORDecodeContext *pMe, const char *szLabel)
+{
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+   MapSearchInfo  Info;
+   QCBORItem      OneItemSeach[2];
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+   OneItemSeach[0].uDataType    = QCBOR_TYPE_ANY;
+   OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE; // Indicates end of array
+
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL);
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset);
+   }
+#else
+   (void)pMe;
+   (void)szLabel;
+   pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+}
+
+
+void
+QCBORDecode_Private_GetItemInMapNoCheck(QCBORDecodeContext *pMe,
+                                        QCBORItem          *OneItemSeach,
+                                        QCBORItem          *pItem,
+                                        size_t             *puOffset)
+{
+   QCBORError    uErr;
+   MapSearchInfo SearchInfo;
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   uErr = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &SearchInfo, NULL);
+
+   if(uErr == QCBOR_SUCCESS && OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
+      uErr = QCBOR_ERR_LABEL_NOT_FOUND;
+   }
+   *pItem = OneItemSeach[0];
+   *puOffset = SearchInfo.uStartOffset;
+
+   if(uErr == QCBOR_SUCCESS) {
+      QCBORDecode_Private_SaveTagNumbers(pMe, pItem);
+   }
+
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+static void
+QCBORDecode_Private_GetItemInMap(QCBORDecodeContext *pMe, QCBORItem *OneItemSeach, QCBORItem *pItem)
+{
+   QCBORError  uErr;
+   size_t      uOffset;
+
+   QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, &uOffset);
+
+   uErr = QCBORDecode_Private_GetItemChecks(pMe, pMe->uLastError, uOffset, pItem);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   QCBORDecode_Private_SaveTagNumbers(pMe, pItem);
+
+Done:
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+void
+QCBORDecode_GetItemInMapN(QCBORDecodeContext *pMe,
+                          const int64_t       nLabel,
+                          const uint8_t       uQcborType,
+                          QCBORItem          *pItem)
+{
    QCBORItem OneItemSeach[2];
+
    OneItemSeach[0].uLabelType  = QCBOR_TYPE_INT64;
    OneItemSeach[0].label.int64 = nLabel;
    OneItemSeach[0].uDataType   = uQcborType;
    OneItemSeach[1].uLabelType  = QCBOR_TYPE_NONE; // Indicates end of array
 
-   QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, NULL, NULL);
+   QCBORDecode_Private_GetItemInMap(pMe, OneItemSeach, pItem);
+}
 
-   if(uReturn != QCBOR_SUCCESS) {
-      pItem->uDataType  = QCBOR_TYPE_NONE;
-      pItem->uLabelType = QCBOR_TYPE_NONE;
-      goto Done;
-   }
 
-   if(OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
-      uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
-   }
+/**
+ * @brief Get an item by label by type.
+ *
+ * @param[in] pMe         The decode context.
+ * @param[in] nLabel      The label to search map for.
+ * @param[in] uQcborType  The QCBOR type to look for.
+ * @param[out] pItem      The item found.
+ * @param[out] puOffset   The offset of item for tag consumption check.
+ *
+ * This finds the item with the given label in currently open
+ * map. This does not call QCBORDecode_Private_GetItemChecks()
+ * to check tag number consumption or decode conformance.
+ */
+static void
+QCBORDecode_GetItemInMapNoCheckN(QCBORDecodeContext *pMe,
+                                 const int64_t       nLabel,
+                                 const uint8_t       uQcborType,
+                                 QCBORItem          *pItem,
+                                 size_t             *puOffset)
+{
+   QCBORItem OneItemSeach[2];
 
-   *pItem = OneItemSeach[0];
-   QCBORDecode_Private_CopyTags(pMe, pItem);
+   OneItemSeach[0].uLabelType  = QCBOR_TYPE_INT64;
+   OneItemSeach[0].label.int64 = nLabel;
+   OneItemSeach[0].uDataType   = uQcborType;
+   OneItemSeach[1].uLabelType  = QCBOR_TYPE_NONE; // Indicates end of array
 
- Done:
-   pMe->uLastError = (uint8_t)uReturn;
+   QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach,  pItem, puOffset);
 }
 
 
@@ -3953,47 +3644,69 @@
 void
 QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe,
                            const char         *szLabel,
-                           uint8_t             uQcborType,
+                           const uint8_t       uQcborType,
                            QCBORItem          *pItem)
 {
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
-
 #ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    QCBORItem OneItemSeach[2];
+
    OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
    OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
    OneItemSeach[0].uDataType    = uQcborType;
    OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE; // Indicates end of array
 
-   QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, NULL, NULL);
+   QCBORDecode_Private_GetItemInMap(pMe, OneItemSeach, pItem);
 
-   if(uReturn != QCBOR_SUCCESS) {
-      pItem->uDataType  = QCBOR_TYPE_NONE;
-      pItem->uLabelType = QCBOR_TYPE_NONE;
-      goto Done;
-   }
-   if(OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
-      uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
-      goto Done;
-   }
-
-   *pItem = OneItemSeach[0];
-   QCBORDecode_Private_CopyTags(pMe, pItem);
-
-Done:
 #else
    (void)pMe;
    (void)szLabel;
    (void)uQcborType;
    (void)pItem;
-   QCBORError uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
+   pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND;
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
-
-   pMe->uLastError = (uint8_t)uReturn;
 }
 
+/**
+ * @brief Get an item by string label of a particular type
+ *
+ * @param[in] pMe         The decode context.
+ * @param[in] szLabel     The label to search map for.
+ * @param[in] uQcborType  The QCBOR type to look for.
+ * @param[out] pItem      The item found.
+ * @param[out] puOffset   The offset of item for tag consumption check.
+ *
+ * This finds the item with the given label in currently open
+ * map. This does not call QCBORDecode_Private_GetItemChecks()
+ * to check tag number consumption or decode conformance.
+ */
+static void
+QCBORDecode_GetItemInMapNoCheckSZ(QCBORDecodeContext *pMe,
+                                  const char         *szLabel,
+                                  const uint8_t       uQcborType,
+                                  QCBORItem          *pItem,
+                                  size_t             *puOffset)
+{
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+   QCBORItem OneItemSeach[2];
+
+   OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+   OneItemSeach[0].uDataType    = uQcborType;
+   OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE; // Indicates end of array
+
+   QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, puOffset);
+
+#else
+   (void)pMe;
+   (void)szLabel;
+   (void)uQcborType;
+   (void)pItem;
+   (void)puOffset;
+   pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+}
+
+
 
 
 /**
@@ -4124,6 +3837,10 @@
    if(pMe->uLastError != QCBOR_SUCCESS) {
       return;
    }
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_GetItemChecks(pMe, pMe->uLastError, Info.uStartOffset, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
 
    /* Save the whole position of things so they can be restored.
     * so the cursor position is unchanged by this operation, like
@@ -4142,179 +3859,50 @@
 
 
 
-/**
- * @brief Is a QCBOR_TYPE in the type list?
- *
- * @param[in] uDataType  Type to check for.
- * @param[in] puTypeList  List to check.
- *
- * @retval  QCBOR_SUCCESS             If in the list.
- * @retval QCBOR_ERR_UNEXPECTED_TYPE  Not in the list.
- */
-static QCBORError
-QCBOR_Private_CheckTypeList(const int     uDataType,
-                            const uint8_t puTypeList[QCBOR_TAGSPEC_NUM_TYPES])
-{
-   for(size_t i = 0; i < QCBOR_TAGSPEC_NUM_TYPES; i++) {
-      if(uDataType == puTypeList[i]) { /* -Wmaybe-uninitialized falsly warns here */
-         return QCBOR_SUCCESS;
-      }
-   }
-   return QCBOR_ERR_UNEXPECTED_TYPE;
-}
-
-
-/**
- * Match a tag/type specification against the type of the item.
- *
- * @param[in] TagSpec  Specification for matching tags.
- * @param[in] pItem    The item to check.
- *
- * @retval QCBOR_SUCCESS   \c uDataType is allowed by @c TagSpec
- * @retval QCBOR_ERR_UNEXPECTED_TYPE \c uDataType is not allowed by @c TagSpec
- *
- * This checks the item data type of untagged items as well as of
- * tagged items against a specification to see if decoding should
- * proceed.
- *
- * This relies on the automatic tag decoding done by QCBOR that turns
- * tag numbers into particular QCBOR_TYPEs so there is no actual
- * comparsion of tag numbers, just of QCBOR_TYPEs.
- *
- * This checks the data item type as possibly representing the tag
- * number or as the tag content type.
- *
- * If QCBOR_DISABLE_TAGS is #defined,  this primarily checks the item
- * data type against the allowed tag content types. It will also error out
- * if the caller tries to require a tag because there is no way that can
- * ever be fulfilled.
- */
-static QCBORError
-QCBOR_Private_CheckTagRequirement(const QCBOR_Private_TagSpec TagSpec,
-                                  const QCBORItem            *pItem)
-{
-   const int nItemType = pItem->uDataType; /* -Wmaybe-uninitialized falsly warns here */
-   const int nTagReq = TagSpec.uTagRequirement & ~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS;
-
-#ifndef QCBOR_DISABLE_TAGS
-   /* -Wmaybe-uninitialized falsly warns here */
-   if(!(TagSpec.uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS) &&
-      pItem->uTags[0] != CBOR_TAG_INVALID16) {
-      /* There are tags that QCBOR couldn't process on this item and
-       * the caller has told us there should not be.
-       */
-      return QCBOR_ERR_UNEXPECTED_TYPE;
-   }
-
-   if(nTagReq == QCBOR_TAG_REQUIREMENT_TAG) {
-      /* Must match the tag number and only the tag */
-      return QCBOR_Private_CheckTypeList(nItemType, TagSpec.uTaggedTypes);
-   }
-
-   QCBORError uReturn = QCBOR_Private_CheckTypeList(nItemType, TagSpec.uAllowedContentTypes);
-   if(uReturn == QCBOR_SUCCESS) {
-      return QCBOR_SUCCESS;
-   }
-
-   if(nTagReq == QCBOR_TAG_REQUIREMENT_NOT_A_TAG) {
-      /* Must match the content type and only the content type.
-       * There was no match just above so it is a fail. */
-      return QCBOR_ERR_UNEXPECTED_TYPE;
-   }
-
-   /* QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG: If here it can match either the tag or the content
-    * and it hasn't matched the content, so the end
-    * result is whether it matches the tag. This is
-    * the tag optional case that the CBOR standard discourages.
-    */
-
-   return QCBOR_Private_CheckTypeList(nItemType, TagSpec.uTaggedTypes);
-
-#else /* QCBOR_DISABLE_TAGS */
-   if(nTagReq == QCBOR_TAG_REQUIREMENT_TAG) {
-      return QCBOR_ERR_UNEXPECTED_TYPE;
-   }
-
-   return QCBOR_Private_CheckTypeList(nItemType, TagSpec.uAllowedContentTypes);
-
-#endif /* QCBOR_DISABLE_TAGS */
-}
-
-
-/**
- * @brief Get an item by label to match a tag specification.
- *
- * @param[in] pMe      The decode context.
- * @param[in] nLabel   The label to search map for.
- * @param[in] TagSpec  The tag number specification to match.
- * @param[out] pItem   The item found.
- *
- * This finds the item with the given label in currently open
- * map. Then checks that its tag number and types matches the tag
- * specification. If not, an error is set in the decode context.
- */
 static void
-QCBORDecode_GetTaggedItemInMapN(QCBORDecodeContext         *pMe,
-                                const int64_t               nLabel,
-                                const QCBOR_Private_TagSpec TagSpec,
-                                QCBORItem                  *pItem)
-{
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, pItem);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
-
-   pMe->uLastError = (uint8_t)QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-}
-
-
-/**
- * @brief Get an item by label to match a tag specification.
- *
- * @param[in] pMe      The decode context.
- * @param[in] szLabel   The label to search map for.
- * @param[in] TagSpec  The tag number specification to match.
- * @param[out] pItem   The item found.
- *
- * This finds the item with the given label in currently open
- * map. Then checks that its tag number and types matches the tag
- * specification. If not, an error is set in the decode context.
- */
-static void
-QCBORDecode_GetTaggedItemInMapSZ(QCBORDecodeContext          *pMe,
-                                 const char                  *szLabel,
-                                 const QCBOR_Private_TagSpec  TagSpec,
-                                 QCBORItem                   *pItem)
-{
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, pItem);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
-
-   pMe->uLastError = (uint8_t)QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-}
-
+QCBORDecode_Private_ProcessTagOne(QCBORDecodeContext      *pMe,
+                                  QCBORItem               *pItem,
+                                  const uint8_t            uTagRequirement,
+                                  const uint8_t            uQCBORType,
+                                  const uint64_t           uTagNumber,
+                                  QCBORTagContentCallBack *pfCB,
+                                  size_t                   uOffset);
 
 /**
  * @brief Semi-private to get an string by label to match a tag specification.
  *
- * @param[in] pMe      The decode context.
- * @param[in] nLabel   The label to search map for.
- * @param[in] TagSpec  The tag number specification to match.
- * @param[out] pString   The string found.
+ * @param[in] pMe              The decode context.
+ * @param[in] nLabel           Label to search map for.
+ * @param[in] uTagRequirement  Whether or not tag number is required.
+ *                             See @ref QCBOR_TAG_REQUIREMENT_TAG.
+ * @param[in] uQCBOR_Type      QCBOR type to search for.
+ * @param[in] uTagNumber       Tag number to match.
+ * @param[out] pString         The string found.
  *
  * This finds the string  with the given label in currently open
  * map. Then checks that its tag number and types matches the tag
  * specification. If not, an error is set in the decode context.
  */
 void
-QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext         *pMe,
-                                          const int64_t               nLabel,
-                                          const QCBOR_Private_TagSpec TagSpec,
-                                          UsefulBufC                 *pString)
+QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext  *pMe,
+                                          const int64_t        nLabel,
+                                          const uint8_t        uTagRequirement,
+                                          const uint8_t        uQCBOR_Type,
+                                          const uint64_t       uTagNumber,
+                                          UsefulBufC          *pString)
 {
-   QCBORItem Item;
-   QCBORDecode_GetTaggedItemInMapN(pMe, nLabel, TagSpec, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                    &Item,
+                                     uTagRequirement,
+                                     uQCBOR_Type,
+                                     uTagNumber,
+                                     QCBORDecode_StringsTagCB,
+                                     uOffset);
+
    if(pMe->uLastError == QCBOR_SUCCESS) {
       *pString = Item.val.string;
    }
@@ -4324,22 +3912,39 @@
 /**
  * @brief Semi-private to get an string by label to match a tag specification.
  *
- * @param[in] pMe      The decode context.
- * @param[in] szLabel   The label to search map for.
- * @param[in] TagSpec  The tag number specification to match.
- * @param[out] pString   The string found.
+ * @param[in] pMe              The decode context.
+ * @param[in] szLabel           Label to search map for.
+ * @param[in] uTagRequirement  Whether or not tag number is required.
+ *                             See @ref QCBOR_TAG_REQUIREMENT_TAG.
+ * @param[in] uQCBOR_Type      QCBOR type to search for.
+ * @param[in] uTagNumber       Tag number to match.
+ * @param[out] pString         The string found.
  *
  * This finds the string  with the given label in currently open
  * map. Then checks that its tag number and types matches the tag
  * specification. If not, an error is set in the decode context.
- */void
-QCBORDecode_Private_GetTaggedStringInMapSZ(QCBORDecodeContext         *pMe,
-                                           const char *                szLabel,
-                                           const QCBOR_Private_TagSpec TagSpec,
-                                           UsefulBufC                 *pString)
+  */
+void
+QCBORDecode_Private_GetTaggedStringInMapSZ(QCBORDecodeContext  *pMe,
+                                           const char          *szLabel,
+                                           uint8_t              uTagRequirement,
+                                           uint8_t              uQCBOR_Type,
+                                           uint64_t             uTagNumber,
+                                           UsefulBufC          *pString)
 {
    QCBORItem Item;
-   QCBORDecode_GetTaggedItemInMapSZ(pMe, szLabel, TagSpec, &Item);
+   size_t    uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                   &Item,
+                                   uTagRequirement,
+                                   uQCBOR_Type,
+                                   uTagNumber,
+                                   QCBORDecode_StringsTagCB,
+                                   uOffset);
+
+
    if(pMe->uLastError == QCBOR_SUCCESS) {
       *pString = Item.val.string;
    }
@@ -4352,8 +3957,7 @@
 void
 QCBORDecode_GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemList)
 {
-   QCBORError uErr = QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, NULL);
-   pMe->uLastError = (uint8_t)uErr;
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, NULL);
 }
 
 /*
@@ -4366,15 +3970,106 @@
                                       QCBORItemCallback   pfCB)
 {
    MapSearchCallBack CallBack;
+
    CallBack.pCBContext = pCallbackCtx;
    CallBack.pfCallback = pfCB;
 
-   QCBORError uErr = QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, &CallBack);
-
-   pMe->uLastError = (uint8_t)uErr;
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, &CallBack);
 }
 
 
+#ifndef QCBOR_DISABLE_TAGS
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+QCBORError
+QCBORDecode_GetNextTagNumberInMapN(QCBORDecodeContext *pMe, const int64_t nLabel, uint64_t *puTagNumber)
+{
+   size_t         uOffset;
+   MapSearchInfo  Info;
+   QCBORItem      OneItemSeach[2];
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return pMe->uLastError;
+   }
+
+   OneItemSeach[0].uLabelType  = QCBOR_TYPE_INT64;
+   OneItemSeach[0].label.int64 = nLabel;
+   OneItemSeach[0].uDataType   = QCBOR_TYPE_ANY;
+   OneItemSeach[1].uLabelType  = QCBOR_TYPE_NONE; // Indicates end of array
+
+   QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL);
+
+   uOffset = Info.uStartOffset;
+   if(uOffset == pMe->uTagNumberCheckOffset) {
+      pMe->uTagNumberIndex++;
+   } else {
+      pMe->uTagNumberIndex = 0;
+   }
+
+   *puTagNumber = CBOR_TAG_INVALID64;
+
+   *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex);
+   if(*puTagNumber == CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) {
+      pMe->uTagNumberIndex = QCBOR_ALL_TAGS_PROCESSED;
+   }
+   pMe->uTagNumberCheckOffset = uOffset;
+
+   return uReturn;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+QCBORError
+QCBORDecode_GetNextTagNumberInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, uint64_t *puTagNumber)
+{
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+   size_t         uOffset;
+   MapSearchInfo  Info;
+   QCBORItem      OneItemSeach[2];
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return pMe->uLastError;
+   }
+
+   OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+   OneItemSeach[0].uDataType    = QCBOR_TYPE_ANY;
+   OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE; // Indicates end of array
+
+   QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL);
+
+
+   uOffset = Info.uStartOffset;
+   if(uOffset == pMe->uTagNumberCheckOffset) {
+      pMe->uTagNumberIndex++;
+   } else {
+      pMe->uTagNumberIndex = 0;
+   }
+
+   *puTagNumber = CBOR_TAG_INVALID64;
+
+   *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex);
+   if(*puTagNumber == CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) {
+      pMe->uTagNumberIndex = 255; /* All tags clear for this item */
+   }
+   pMe->uTagNumberCheckOffset = uOffset;
+
+   return uReturn;
+#else
+   (void)pMe;
+   (void)szLabel;
+   (void)puTagNumber;
+   return QCBOR_ERR_LABEL_NOT_FOUND;
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+}
+#endif /* ! QCBOR_DISABLE_TAGS */
+
+
 /**
  * @brief Search for a map/array by label and enter it
  *
@@ -4391,6 +4086,9 @@
 static void
 QCBORDecode_Private_SearchAndEnter(QCBORDecodeContext *pMe, QCBORItem pSearch[])
 {
+   QCBORError     uErr;
+   MapSearchInfo  SearchInfo;
+
    // The first item in pSearch is the one that is to be
    // entered. It should be the only one filled in. Any other
    // will be ignored unless it causes an error.
@@ -4398,8 +4096,10 @@
       return;
    }
 
-   MapSearchInfo Info;
-   pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pSearch, &Info, NULL);
+   uErr = QCBORDecode_Private_MapSearch(pMe, pSearch, &SearchInfo, NULL);
+
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_GetItemChecks(pMe, uErr, SearchInfo.uStartOffset, pSearch);
+
    if(pMe->uLastError != QCBOR_SUCCESS) {
       return;
    }
@@ -4429,7 +4129,7 @@
     * to be used to get one item and MapSearch() has already found it
     * confirming it exists.
     */
-   UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset);
+   UsefulInputBuf_Seek(&(pMe->InBuf), SearchInfo.uStartOffset);
 
    DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting));
 
@@ -4556,7 +4256,7 @@
       goto Done;
    }
 
-   QCBORDecode_Private_CopyTags(pMe, &Item);
+   QCBORDecode_Private_SaveTagNumbers(pMe, &Item);
 
 
    const bool bIsEmpty = (Item.uNextNestLevel <= Item.uNestingLevel);
@@ -4696,6 +4396,16 @@
 }
 
 
+// TODO: re order this file with tags stuff last. bstr is a tag thing
+static QCBORError
+QCBORDecode_Private_CheckTagNType(QCBORDecodeContext *pMe,
+                                  const QCBORItem *pItem,
+                                  const size_t uOffset,
+                                  const uint8_t *uQCBORTypes,
+                                  const uint64_t *uTagNumbers,
+                                  const uint8_t uTagRequirement,
+                                  bool *bTypeMatched);
+
 /**
  * @brief The main work of entering some byte-string wrapped CBOR.
  *
@@ -4714,31 +4424,41 @@
 QCBORDecode_Private_EnterBstrWrapped(QCBORDecodeContext *pMe,
                                      const QCBORItem    *pItem,
                                      const uint8_t       uTagRequirement,
+                                     const size_t        uOffset,
                                      UsefulBufC         *pBstr)
 {
+   bool       bTypeMatched;
+   QCBORError uError;
+
+   const uint8_t uTypes[] = {QBCOR_TYPE_WRAPPED_CBOR, QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE, QCBOR_TYPE_NONE};
+   const uint64_t uTagNumbers[] = {CBOR_TAG_CBOR, CBOR_TAG_CBOR_SEQUENCE, CBOR_TAG_INVALID64};
+
+
    if(pBstr) {
       *pBstr = NULLUsefulBufC;
    }
 
    if(pMe->uLastError != QCBOR_SUCCESS) {
-      /* Already in error state; do nothing. */
       return pMe->uLastError;
    }
 
-   QCBORError uError;
-
-   const QCBOR_Private_TagSpec TagSpec =
-      {
-         uTagRequirement,
-         {QBCOR_TYPE_WRAPPED_CBOR, QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE, QCBOR_TYPE_NONE},
-         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-      };
-
-   uError = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-   if(uError != QCBOR_SUCCESS) {
-      goto Done;
+   if(pItem->uDataAlloc) {
+      return QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING;
    }
 
+   uError = QCBORDecode_Private_CheckTagNType(pMe,
+                                              pItem,
+                                              uOffset,
+                                              uTypes, // TODO: maybe this should be empty
+                                              uTagNumbers,
+                                              uTagRequirement,
+                                             &bTypeMatched);
+
+   if(pItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+      uError = QCBOR_ERR_BAD_TAG_CONTENT; // TODO: error
+   }
+
+
    if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) {
       /* Reverse the decrement done by GetNext() for the bstr so the
        * increment in QCBORDecode_NestLevelAscender() called by
@@ -4790,6 +4510,23 @@
 }
 
 
+static void
+QCBORDecode_Private_GetAndTell(QCBORDecodeContext *pMe, QCBORItem *Item, size_t *uOffset)
+{
+#ifndef QCBOR_DISABLE_TAGS
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   *uOffset = QCBORDecode_Tell(pMe);
+#else
+   *uOffset = SIZE_MAX;
+
+#endif /* ! QCBOR_DISABLE_TAGS */
+   pMe->uLastError = (uint8_t)QCBORDecode_Private_GetNextTagContent(pMe, Item);
+}
+
+
 /*
  * Public function, see header qcbor/qcbor_decode.h file
  */
@@ -4798,26 +4535,14 @@
                              const uint8_t       uTagRequirement,
                              UsefulBufC         *pBstr)
 {
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      // Already in error state; do nothing.
-      return;
-   }
-
-   /* Get the data item that is the byte string being entered */
    QCBORItem Item;
-   pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, &Item);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
+   size_t    uOffset;
 
-   if(Item.uDataAlloc) {
-      pMe->uLastError = QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING;
-      return;
-   }
-
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
    pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe,
                                                                   &Item,
                                                                    uTagRequirement,
+                                                                   uOffset,
                                                                    pBstr);
 }
 
@@ -4832,11 +4557,13 @@
                                      UsefulBufC         *pBstr)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_BYTE_STRING, &Item, &uOffset);
    pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe,
                                                                   &Item,
                                                                    uTagRequirement,
+                                                                   uOffset,
                                                                    pBstr);
 }
 
@@ -4851,11 +4578,13 @@
                                       UsefulBufC         *pBstr)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_BYTE_STRING, &Item, &uOffset);
    pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe,
                                                                   &Item,
                                                                    uTagRequirement,
+                                                                   uOffset,
                                                                    pBstr);
 }
 
@@ -5057,56 +4786,214 @@
 
 
 
-/**
- * @brief Common processing for an epoch date.
- *
- * @param[in] pMe              The decode context.
- * @param[in] pItem            The item with the date.
- * @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
- * @param[out] pnTime          The returned date.
- *
- * Common processing for the date tag. Mostly make sure the tag
- * content is correct and copy forward any further other tag numbers.
- */
-static void
-QCBORDecode_Private_ProcessEpochDate(QCBORDecodeContext *pMe,
-                                     QCBORItem          *pItem,
-                                     const uint8_t       uTagRequirement,
-                                     int64_t            *pnTime)
+
+#ifndef QCBOR_DISABLE_TAGS
+// TODO:  uTagNumber might be better a list than calling this multiple times
+static QCBORError
+QCBORDecode_Private_Check1TagNumber(const QCBORDecodeContext *pMe,
+                                    const QCBORItem          *pItem,
+                                    const uint64_t            uTagNumber,
+                                    const size_t              uOffset)
 {
+   if(pItem->auTagNumbers[0] == CBOR_TAG_INVALID16) {
+      /* There are no tag numbers at all, so no unprocessed */
+      return QCBOR_SUCCESS;
+   }
+
+   /* There are some tag numbers, so keep checking. This check passes
+    * if there is one and only one tag number that matches uTagNumber
+    */
+
+   // TODO: behave different in v1 and v2?
+
+   const uint64_t uInnerTag = QCBORDecode_GetNthTagNumber(pMe, pItem, 0);
+
+   if(uInnerTag == uTagNumber && pItem->auTagNumbers[1] == CBOR_TAG_INVALID16 ) {
+      /* The only tag number is the one we are processing so no unprocessed */
+      return QCBOR_SUCCESS;
+   }
+
+   if(uOffset != pMe->uTagNumberCheckOffset) {
+      /* processed tag numbers are for some other item, not us */
+      return QCBOR_ERR_UNPROCESSED_TAG_NUMBER;
+   }
+
+   if(pMe->uTagNumberIndex != 1) {
+      return QCBOR_ERR_UNPROCESSED_TAG_NUMBER;
+   }
+
+   return QCBOR_SUCCESS;
+}
+#endif
+
+
+static QCBORError
+QCBORDecode_Private_CheckTagNType(QCBORDecodeContext *pMe,
+                                  const QCBORItem    *pItem,
+                                  const size_t        uOffset,
+                                  const uint8_t      *uQCBORTypes,
+                                  const uint64_t     *uTagNumbers,
+                                  const uint8_t       uTagRequirement,
+                                  bool               *bTypeMatched)
+{
+   const int      nTagReq   = uTagRequirement & ~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS;
+
+   *bTypeMatched = false;
+   for(const uint8_t *pTNum = uQCBORTypes; *pTNum != QCBOR_TYPE_NONE; pTNum++) {
+      if(pItem->uDataType == *pTNum) {
+         *bTypeMatched = true;
+         break;
+      }
+   }
+
+#ifndef QCBOR_DISABLE_TAGS
+   bool        bTagNumberMatched;
+   QCBORError  uErr;
+   const uint64_t uInnerTag = QCBORDecode_GetNthTagNumber(pMe, pItem, 0);
+
+   bTagNumberMatched = false;
+   for(const uint64_t *pQType = uTagNumbers; *pQType != CBOR_TAG_INVALID64; pQType++) {
+      if(uInnerTag == *pQType) {
+         bTagNumberMatched = true;
+         break;
+      }
+   }
+
+
+   if(nTagReq == QCBOR_TAG_REQUIREMENT_TAG) {
+      /* There must be a tag number */
+      if(!bTagNumberMatched && !*bTypeMatched) {
+         return QCBOR_ERR_UNEXPECTED_TYPE; // TODO: error code
+      }
+
+   } else if(nTagReq == QCBOR_TAG_REQUIREMENT_NOT_A_TAG) {
+      if(bTagNumberMatched || *bTypeMatched) {
+         return QCBOR_ERR_UNEXPECTED_TYPE; // TODO: error code
+      }
+
+   } else if(nTagReq == QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG) {
+      /* No check necessary */
+   }
+
+   /* Now check if there are extra tags and if there's an error in them */
+   if(!(uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS)) {
+      /* The flag to ignore extra is not set, so keep checking */
+      for(const uint64_t *pTNum = uTagNumbers; *pTNum != CBOR_TAG_INVALID64; pTNum++) {
+         uErr = QCBORDecode_Private_Check1TagNumber(pMe, pItem, *pTNum, uOffset);
+         if(uErr != QCBOR_SUCCESS) {
+            return uErr;
+         }
+      }
+   }
+
+   return QCBOR_SUCCESS;
+#else
+   (void)pMe;
+   (void)uOffset;
+   (void)uTagNumbers;
+
+   if(nTagReq != QCBOR_TAG_REQUIREMENT_TAG && bTypeMatched) {
+      return QCBOR_SUCCESS;
+   } else {
+      return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+#endif
+
+}
+
+
+void
+QCBORDecode_Private_ProcessTagItemMulti(QCBORDecodeContext      *pMe,
+                                        QCBORItem               *pItem,
+                                        const uint8_t            uTagRequirement,
+                                        const uint8_t            uQCBORTypes[],
+                                        const uint64_t           uTagNumbers[],
+                                        QCBORTagContentCallBack *pfCB,
+                                        size_t                   uOffset)
+{
+   QCBORError uErr;
+   bool       bTypeMatched;
+
    if(pMe->uLastError != QCBOR_SUCCESS) {
-      // Already in error state, do nothing
       return;
    }
 
-   QCBORError uErr;
-
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DATE_EPOCH, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_INT64, QCBOR_TYPE_DOUBLE, QCBOR_TYPE_FLOAT, QCBOR_TYPE_UINT64}
-   };
-
-   uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
+   uErr = QCBORDecode_Private_CheckTagNType(pMe,
+                                            pItem,
+                                            uOffset,
+                                            uQCBORTypes,
+                                            uTagNumbers,
+                                            uTagRequirement,
+                                            &bTypeMatched);
    if(uErr != QCBOR_SUCCESS) {
       goto Done;
    }
 
-   if(pItem->uDataType != QCBOR_TYPE_DATE_EPOCH) {
-      uErr = QCBOR_Private_DecodeDateEpoch(pItem);
+   if(!bTypeMatched) {
+      /* Tag content wasn't previously processed, do it now */
+      uErr = (*pfCB)(pMe, NULL, uTagNumbers[0], pItem);
       if(uErr != QCBOR_SUCCESS) {
          goto Done;
       }
    }
 
-   *pnTime = pItem->val.epochDate.nSeconds;
-
 Done:
    pMe->uLastError = (uint8_t)uErr;
 }
 
 
+/*
+ **/
+ void
+QCBORDecode_Private_ProcessTagItem(QCBORDecodeContext      *pMe,
+                                   QCBORItem               *pItem,
+                                   const uint8_t            uTagRequirement,
+                                   const uint8_t            uQCBORTypes[],
+                                   const uint64_t           uTagNumber,
+                                   QCBORTagContentCallBack *pfCB,
+                                   size_t                   uOffset)
+{
+   uint64_t auTagNumbers[2];
+
+   auTagNumbers[0] = uTagNumber;
+   auTagNumbers[1] = CBOR_TAG_INVALID64;
+
+   QCBORDecode_Private_ProcessTagItemMulti(pMe,
+                                           pItem,
+                                           uTagRequirement,
+                                           uQCBORTypes,
+                                           auTagNumbers,
+                                           pfCB,
+                                           uOffset);
+}
+
+
+static void
+QCBORDecode_Private_ProcessTagOne(QCBORDecodeContext *pMe,
+                                  QCBORItem          *pItem,
+                                  const uint8_t       uTagRequirement,
+                                  const uint8_t       uQCBORType,
+                                  const uint64_t      uTagNumber,
+                                  QCBORTagContentCallBack *pfCB,
+                                  const size_t        uOffset)
+{
+   uint8_t auQCBORType[2];
+
+   auQCBORType[0] = uQCBORType;
+   auQCBORType[1] = QCBOR_TYPE_NONE;
+
+   QCBORDecode_Private_ProcessTagItem(pMe,
+                                      pItem,
+                                      uTagRequirement,
+                                      auQCBORType,
+                                      uTagNumber,
+                                      pfCB,
+                                      uOffset);
+}
+
+
+
 
 /*
  * Public function, see header qcbor/qcbor_spiffy_decode.h file
@@ -5117,8 +5004,17 @@
                          int64_t            *pnTime)
 {
    QCBORItem  Item;
-   QCBORDecode_VGetNext(pMe, &Item);
-   QCBORDecode_Private_ProcessEpochDate(pMe, &Item, uTagRequirement, pnTime);
+   size_t     uOffset;
+
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                     &Item,
+                                     uTagRequirement,
+                                     QCBOR_TYPE_DATE_EPOCH,
+                                     CBOR_TAG_DATE_EPOCH,
+                                     QCBORDecode_DateEpochTagCB,
+                                     uOffset);
+   *pnTime = Item.val.epochDate.nSeconds;
 }
 
 
@@ -5132,8 +5028,17 @@
                                int64_t            *pnTime)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
-   QCBORDecode_Private_ProcessEpochDate(pMe, &Item, uTagRequirement, pnTime);
+   size_t uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                     &Item,
+                                     uTagRequirement,
+                                     QCBOR_TYPE_DATE_EPOCH,
+                                     CBOR_TAG_DATE_EPOCH,
+                                     QCBORDecode_DateEpochTagCB,
+                                     uOffset);
+   *pnTime = Item.val.epochDate.nSeconds;
 }
 
 
@@ -5147,60 +5052,17 @@
                                 int64_t            *pnTime)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
-   QCBORDecode_Private_ProcessEpochDate(pMe, &Item, uTagRequirement, pnTime);
-}
+   size_t uOffset;
 
-
-
-/**
- * @brief Common processing for an epoch date.
- *
- * @param[in] pMe              The decode context.
- * @param[in] pItem            The item with the date.
- * @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
- * @param[out] pnDays          The returned day count.
- *
- * Common processing for the RFC 8943 day-count tag. Mostly make sure
- * the tag content is correct and copy forward any further other tag
- * numbers.
- */
-static void
-QCBORDecode_Private_ProcessEpochDays(QCBORDecodeContext *pMe,
-                                     QCBORItem          *pItem,
-                                     uint8_t             uTagRequirement,
-                                     int64_t            *pnDays)
-{
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      /* Already in error state, do nothing */
-      return;
-   }
-
-   QCBORError uErr;
-
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DAYS_EPOCH, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_INT64, QCBOR_TYPE_UINT64, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
-   uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-   if(uErr != QCBOR_SUCCESS) {
-      goto Done;
-   }
-
-   if(pItem->uDataType != QCBOR_TYPE_DAYS_EPOCH) {
-      uErr = QCBOR_Private_DecodeDaysEpoch(pItem);
-      if(uErr != QCBOR_SUCCESS) {
-         goto Done;
-      }
-   }
-
-   *pnDays = pItem->val.epochDays;
-
-Done:
-   pMe->uLastError = (uint8_t)uErr;
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                     &Item,
+                                     uTagRequirement,
+                                     QCBOR_TYPE_DATE_EPOCH,
+                                     CBOR_TAG_DATE_EPOCH,
+                                     QCBORDecode_DateEpochTagCB,
+                                     uOffset);
+   *pnTime = Item.val.epochDate.nSeconds;
 }
 
 
@@ -5213,8 +5075,17 @@
                          int64_t            *pnDays)
 {
    QCBORItem  Item;
-   QCBORDecode_VGetNext(pMe, &Item);
-   QCBORDecode_Private_ProcessEpochDays(pMe, &Item, uTagRequirement, pnDays);
+   size_t     uOffset;
+
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                     &Item,
+                                     uTagRequirement,
+                                     QCBOR_TYPE_DAYS_EPOCH,
+                                     CBOR_TAG_DAYS_EPOCH,
+                                     QCBORDecode_DaysEpochTagCB,
+                                     uOffset);
+   *pnDays = Item.val.epochDays;
 }
 
 
@@ -5228,8 +5099,17 @@
                                int64_t            *pnDays)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
-   QCBORDecode_Private_ProcessEpochDays(pMe, &Item, uTagRequirement, pnDays);
+   size_t uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                     &Item,
+                                     uTagRequirement,
+                                     QCBOR_TYPE_DAYS_EPOCH,
+                                     CBOR_TAG_DAYS_EPOCH,
+                                     QCBORDecode_DaysEpochTagCB,
+                                     uOffset);
+   *pnDays = Item.val.epochDays;
 }
 
 
@@ -5243,33 +5123,45 @@
                                 int64_t            *pnDays)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
-   QCBORDecode_Private_ProcessEpochDays(pMe, &Item, uTagRequirement, pnDays);
+   size_t    uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                      &Item,
+                                      uTagRequirement,
+                                      QCBOR_TYPE_DAYS_EPOCH,
+                                      CBOR_TAG_DAYS_EPOCH,
+                                      QCBORDecode_DaysEpochTagCB,
+                                      uOffset);
+   *pnDays = Item.val.epochDays;
 }
 
 
 
-/*
- * @brief Get a string that matches the type/tag specification.
- */
+
 void
-QCBORDecode_Private_GetTaggedString(QCBORDecodeContext         *pMe,
-                                    const QCBOR_Private_TagSpec TagSpec,
-                                    UsefulBufC                 *pBstr)
+QCBORDecode_Private_GetTaggedString(QCBORDecodeContext  *pMe,
+                                    const uint8_t        uTagRequirement,
+                                    const uint8_t        uQCBOR_Type,
+                                    const uint64_t       uTagNumber,
+                                    UsefulBufC          *pStr)
 {
    QCBORItem  Item;
+   size_t uOffset;
 
-   QCBORDecode_VGetNext(pMe, &Item);
-   if(pMe->uLastError) {
-      return;
-   }
-
-   pMe->uLastError = (uint8_t)QCBOR_Private_CheckTagRequirement(TagSpec, &Item);
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBORDecode_Private_ProcessTagOne(pMe,
+                                     &Item,
+                                      uTagRequirement,
+                                      uQCBOR_Type,
+                                      uTagNumber,
+                                      QCBORDecode_StringsTagCB,
+                                      uOffset);
 
    if(pMe->uLastError == QCBOR_SUCCESS) {
-      *pBstr = Item.val.string;
+      *pStr = Item.val.string;
    } else {
-      *pBstr = NULLUsefulBufC;
+      *pStr = NULLUsefulBufC;
    }
 }
 
@@ -5288,33 +5180,138 @@
  * the tag content is correct and copy forward any further other tag
  * numbers.
  */
-static QCBORError
-QCBOR_Private_ProcessBigNum(const uint8_t    uTagRequirement,
-                            const QCBORItem *pItem,
-                            UsefulBufC      *pValue,
-                            bool            *pbIsNegative)
+static void
+QCBOR_Private_ProcessBigNum(QCBORDecodeContext *pMe,
+                             const uint8_t       uTagRequirement,
+                             QCBORItem          *pItem,
+                             UsefulBufC         *pValue,
+                             bool               *pbIsNegative,
+                             size_t              uOffset)
 {
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_POSBIGNUM, QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
+   QCBORError uErr;
 
-   QCBORError uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-   if(uErr != QCBOR_SUCCESS) {
-      return uErr;
+   const uint8_t puTypes[] = {QCBOR_TYPE_POSBIGNUM,QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE};
+
+   const uint64_t puTNs[] = {CBOR_TAG_POS_BIGNUM, CBOR_TAG_NEG_BIGNUM, CBOR_TAG_INVALID64};
+
+   QCBORDecode_Private_ProcessTagItemMulti(pMe,
+                                           pItem,
+                                           uTagRequirement,
+                                           puTypes,
+                                           puTNs, QCBORDecode_StringsTagCB, uOffset);
+   if(pMe->uLastError) {
+      return;
    }
 
-   *pValue = pItem->val.string;
 
+   // TODO: is this right? Tests are passing. Fix after merges.
+   // TODO: make this work for GetBigNum and GetBigNumber(). They are different.
    if(pItem->uDataType == QCBOR_TYPE_POSBIGNUM) {
       *pbIsNegative = false;
    } else if(pItem->uDataType == QCBOR_TYPE_NEGBIGNUM) {
       *pbIsNegative = true;
    }
+   *pValue = pItem->val.bigNum;
 
-   return QCBOR_SUCCESS;
+   uErr = QCBOR_SUCCESS;
+
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+static void
+QCBORDecode_Private_GetMIME(QCBORDecodeContext *pMe,
+                            const uint8_t       uTagRequirement,
+                            QCBORItem          *pItem,
+                            UsefulBufC         *pValue,
+                            bool               *pbIsTag257,
+                            size_t              uOffset)
+{
+   QCBORError uErr;
+
+   const uint8_t puTypes[] = {QCBOR_TYPE_MIME, QCBOR_TYPE_BINARY_MIME, QCBOR_TYPE_NONE};
+
+   const uint64_t puTNs[] = {CBOR_TAG_MIME, CBOR_TAG_BINARY_MIME, CBOR_TAG_INVALID64};
+
+   QCBORDecode_Private_ProcessTagItemMulti(pMe,
+                                           pItem,
+                                           uTagRequirement,
+                                           puTypes,
+                                           puTNs,
+                                           QCBORDecode_MIMETagCB,
+                                           uOffset);
+   if(pMe->uLastError) {
+      return;
+   }
+
+   if(pItem->uDataType == QCBOR_TYPE_MIME) {
+      *pbIsTag257 = false;
+   } else if(pItem->uDataType == QCBOR_TYPE_BINARY_MIME) {
+      *pbIsTag257 = true;
+   }
+   *pValue = pItem->val.string;
+
+
+   uErr = QCBOR_SUCCESS;
+
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+void
+QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pMe,
+                           const uint8_t       uTagRequirement,
+                           UsefulBufC         *pMessage,
+                           bool               *pbIsTag257)
+{
+   QCBORItem  Item;
+   size_t     uOffset;
+
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBORDecode_Private_GetMIME(pMe,
+                               uTagRequirement,
+                              &Item,
+                               pMessage,
+                               pbIsTag257,
+                               uOffset);
+}
+
+void
+QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pMe,
+                                 const int64_t       nLabel,
+                                 const uint8_t       uTagRequirement,
+                                 UsefulBufC         *pMessage,
+                                 bool               *pbIsTag257)
+{
+   QCBORItem  Item;
+   size_t     uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_GetMIME(pMe,
+                                uTagRequirement,
+                               &Item,
+                                pMessage,
+                                pbIsTag257,
+                                uOffset);
+}
+
+void
+QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pMe,
+                                  const char         *szLabel,
+                                  const uint8_t       uTagRequirement,
+                                  UsefulBufC         *pMessage,
+                                  bool               *pbIsTag257)
+{
+   QCBORItem  Item;
+   size_t     uOffset;
+
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBORDecode_Private_GetMIME(pMe,
+                                uTagRequirement,
+                               &Item,
+                                pMessage,
+                                pbIsTag257,
+                                uOffset);
 }
 
 
@@ -5328,19 +5325,17 @@
                       bool               *pbIsNegative)
 {
    QCBORItem  Item;
+   size_t     uOffset;
 
-   QCBORDecode_VGetNext(pMe, &Item);
-   if(pMe->uLastError) {
-      return;
-   }
-
-   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessBigNum(uTagRequirement,
-                                                          &Item,
-                                                          pValue,
-                                                          pbIsNegative);
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBOR_Private_ProcessBigNum(pMe,
+                               uTagRequirement,
+                              &Item,
+                               pValue,
+                               pbIsNegative,
+                               uOffset);
 }
 
-
 /*
  * Public function, see header qcbor/qcbor_spiffy_decode.h
  */
@@ -5351,19 +5346,18 @@
                             UsefulBufC         *pValue,
                             bool               *pbIsNegative)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessBigNum(uTagRequirement,
-                                                          &Item,
-                                                          pValue,
-                                                          pbIsNegative);
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBOR_Private_ProcessBigNum(pMe,
+                                uTagRequirement,
+                               &Item,
+                                pValue,
+                                pbIsNegative,
+                                uOffset);
 }
 
-
 /*
  * Public function, see header qcbor/qcbor_spiffy_decode.h
  */
@@ -5374,73 +5368,20 @@
                              UsefulBufC         *pValue,
                              bool               *pbIsNegative)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessBigNum(uTagRequirement,
-                                                          &Item,
-                                                          pValue,
-                                                          pbIsNegative);
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBOR_Private_ProcessBigNum(pMe,
+                                uTagRequirement,
+                               &Item,
+                                pValue,
+                                pbIsNegative,
+                                uOffset);
 }
 
 
 
-/**
- * @brief Common processing for MIME tag (semi-private).
- *
- * @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
- * @param[in] pItem            The item with the date.
- * @param[out] pMessage        The returned MIME message.
- * @param[out] pbIsTag257      If true, binary MIME, if not, text MIME.
- *
- * Common processing for the MIME tag. Mostly make sure the tag
- * content is correct and copy forward any further other tag
- * numbers. See QCBORDecode_GetMIMEMessage().
- */
-QCBORError
-QCBORDecode_Private_GetMIME(const uint8_t     uTagRequirement,
-                            const QCBORItem  *pItem,
-                            UsefulBufC       *pMessage,
-                            bool             *pbIsTag257)
-{
-   const QCBOR_Private_TagSpec TagSpecText =
-      {
-         uTagRequirement,
-         {QCBOR_TYPE_MIME, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
-         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-      };
-   const QCBOR_Private_TagSpec TagSpecBinary =
-      {
-         uTagRequirement,
-         {QCBOR_TYPE_BINARY_MIME, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
-         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-      };
-
-   QCBORError uReturn;
-
-   if(QCBOR_Private_CheckTagRequirement(TagSpecText, pItem) == QCBOR_SUCCESS) {
-      *pMessage = pItem->val.string;
-      if(pbIsTag257 != NULL) {
-         *pbIsTag257 = false;
-      }
-      uReturn = QCBOR_SUCCESS;
-   } else if(QCBOR_Private_CheckTagRequirement(TagSpecBinary, pItem) == QCBOR_SUCCESS) {
-      *pMessage = pItem->val.string;
-      if(pbIsTag257 != NULL) {
-         *pbIsTag257 = true;
-      }
-      uReturn = QCBOR_SUCCESS;
-
-   } else {
-      uReturn = QCBOR_ERR_UNEXPECTED_TYPE;
-   }
-
-   return uReturn;
-}
-
 // Improvement: add methods for wrapped CBOR, a simple alternate
 // to EnterBstrWrapped
 
@@ -7080,78 +7021,10 @@
 }
 
 
-/**
- * @brief Check and/or complete exponent and mantissa item.
- *
- * @param[in] pMe        The decoder context.
- * @param[in] TagSpec    Expected type(s).
- * @param[in,out] pItem  See below.
- *
- * This is for decimal fractions and big floats, both of which are an
- * exponent and mantissa.
- *
- * If the item item had a tag number indicating it was a
- * decimal fraction or big float, then the input @c pItem will
- * have been decoded as exponent and mantissa. If there was
- * no tag number, the caller is asking this be decoded as a
- * big float or decimal fraction and @c pItem just has the
- * first item in an exponent and mantissa.
- *
- * On output, the item is always a fully decoded decimal fraction or
- * big float.
- *
- * This errors out if the input type does not meet the TagSpec.
- */
-static QCBORError
-QCBOR_Private_ExpMantissaTypeHandler(QCBORDecodeContext         *pMe,
-                                     const QCBOR_Private_TagSpec TagSpec,
-                                     QCBORItem                  *pItem)
-{
-   QCBORError uErr;
-
-   /* pItem could either be a decoded exponent and mantissa or
-    * the opening array of an undecoded exponent and mantissa. This
-    * check will succeed on either, but doesn't say which it was.
-    */
-   uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-   if(uErr != QCBOR_SUCCESS) {
-      goto Done;
-   }
-
-   if(pItem->uDataType == QCBOR_TYPE_ARRAY) {
-      /* The item is an array, which means is is an undecoded exponent
-       * and mantissa. This call consumes the items in the array and
-       * results in a decoded exponent and mantissa in pItem. This is
-       * the case where there was no tag.
-       */
-      uErr = QCBORDecode_Private_ExpMantissa(pMe, pItem);
-      if(uErr != QCBOR_SUCCESS) {
-         goto Done;
-      }
-
-      /* The above decode didn't determine whether it is a decimal
-       * fraction or big num. Which of these two depends on what the
-       * caller wants it decoded as since there is no tag, so fish the
-       * type out of the TagSpec. */
-      pItem->uDataType = QCBOR_Private_ExpMantissaDataType(TagSpec.uTaggedTypes[0], pItem);
-
-      /* No need to check the type again. All that we need to know was
-       * that it decoded correctly as a exponent and mantissa. The
-       * QCBOR type is set out by what was requested.
-       */
-   }
-
-   /* If the item was not an array and the check passed, then
-    * it is a fully decoded big float or decimal fraction and
-    * matches what is requested.
-    */
-
-Done:
-   return uErr;
-}
 
 
 /* Some notes from the work to disable tags.
+ * Some are out of date since tag refactoring.
  *
  * The API for big floats and decimal fractions seems good.
  * If there's any issue with it it's that the code size to
@@ -7182,11 +7055,26 @@
  * vary by a TagSpec.
  */
 
+
+static const uint8_t QCBORDecode_Private_DecimalFractionTypes[] = {
+   QCBOR_TYPE_DECIMAL_FRACTION,
+   QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
+   QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM,
+   QCBOR_TYPE_NONE};
+
+static const uint8_t QCBORDecode_Private_BigFloatTypes[] = {
+   QCBOR_TYPE_BIGFLOAT,
+   QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
+   QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM,
+   QCBOR_TYPE_NONE};
+
 /**
  * @brief Common processor for exponent and mantissa.
  *
  * @param[in] pMe          The decode context.
- * @param[in] TagSpec      The expected/allowed tags.
+ * @param[in] uTagRequirement  Whether tag number must be present or not.
+ * @param[in] uTagNumber   The tag number for which content is expected.
+ * @param[in] uOffset   Cursor offset for  tag number consumption checking.
  * @param[in] pItem        The data item to process.
  * @param[out] pnMantissa  The returned mantissa as an int64_t.
  * @param[out] pnExponent  The returned exponent as an int64_t.
@@ -7194,23 +7082,44 @@
  * This handles exponent and mantissa for base 2 and 10. This
  * is limited to a mantissa that is an int64_t. See also
  * QCBORDecode_Private_ProcessExpMantissaBig().
+ *
+ * On output, the item is always a fully decoded decimal fraction or
+ * big float.
+ *
+ * This errors out if the input type does not meet the TagSpec.
  */
 static void
 QCBOR_Private_ProcessExpMantissa(QCBORDecodeContext         *pMe,
-                                 const QCBOR_Private_TagSpec TagSpec,
+                                 const uint8_t               uTagRequirement,
+                                 const uint64_t              uTagNumber,
+                                 const size_t                uOffset,
                                  QCBORItem                  *pItem,
                                  int64_t                    *pnMantissa,
                                  int64_t                    *pnExponent)
 {
-   QCBORError uErr;
+   QCBORError uErr = QCBOR_SUCCESS;
+   const uint8_t *qTypes;
 
    if(pMe->uLastError) {
       return;
    }
 
-   uErr = QCBOR_Private_ExpMantissaTypeHandler(pMe, TagSpec, pItem);
-   if(uErr != QCBOR_SUCCESS) {
-      goto Done;
+   if(uTagNumber == CBOR_TAG_BIGFLOAT) {
+      qTypes = QCBORDecode_Private_BigFloatTypes;
+   } else {
+      qTypes = QCBORDecode_Private_DecimalFractionTypes;
+   }
+
+   QCBORDecode_Private_ProcessTagItem(pMe,
+                                      pItem,
+                                      uTagRequirement,
+                                      qTypes,
+                                      uTagNumber,
+                                      QCBORDecode_ExpMantissaTagCB,
+                                      uOffset);
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
    }
 
    switch (pItem->uDataType) {
@@ -7240,12 +7149,12 @@
          uErr = QCBOR_ERR_UNEXPECTED_TYPE;
    }
 
-   Done:
+  // Done:
       pMe->uLastError = (uint8_t)uErr;
 }
 
 
-/**
+/*
  * @brief Decode exponent and mantissa into a big number.
  *
  * @param[in] pMe                The decode context.
@@ -7261,23 +7170,39 @@
  * that make up the decimal fraction or big float.
  */
 static void
-QCBORDecode_Private_ProcessExpMantissaBig(QCBORDecodeContext          *pMe,
-                                          const QCBOR_Private_TagSpec  TagSpec,
-                                          QCBORItem                   *pItem,
-                                          const UsefulBuf              BufferForMantissa,
-                                          UsefulBufC                  *pMantissa,
-                                          bool                        *pbIsNegative,
-                                          int64_t                     *pnExponent)
+QCBORDecode_Private_ProcessExpMantissaBig(QCBORDecodeContext  *pMe,
+                                          const uint8_t        uTagRequirement,
+                                          const uint64_t       uTagNumber,
+                                          const size_t         uOffset,
+                                          QCBORItem           *pItem,
+                                          const UsefulBuf      BufferForMantissa,
+                                          UsefulBufC          *pMantissa,
+                                          bool                *pbIsNegative,
+                                          int64_t             *pnExponent)
 {
-   QCBORError uErr;
+   QCBORError uErr = QCBOR_SUCCESS;
+   const uint8_t *qTypes;
 
-   if(pMe->uLastError != QCBOR_SUCCESS) {
+   if(pMe->uLastError) {
       return;
    }
 
-   uErr = QCBOR_Private_ExpMantissaTypeHandler(pMe, TagSpec, pItem);
-   if(uErr != QCBOR_SUCCESS) {
-      goto Done;
+   if(uTagNumber == CBOR_TAG_BIGFLOAT) {
+      qTypes = QCBORDecode_Private_BigFloatTypes;
+   } else {
+      qTypes = QCBORDecode_Private_DecimalFractionTypes;
+   }
+
+   QCBORDecode_Private_ProcessTagItem(pMe,
+                                      pItem,
+                                      uTagRequirement,
+                                      qTypes,
+                                      uTagNumber,
+                                      QCBORDecode_ExpMantissaTagCB,
+                                      uOffset);
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
    }
 
    uint64_t uMantissa;
@@ -7323,7 +7248,6 @@
          uErr = QCBOR_ERR_UNEXPECTED_TYPE;
    }
 
-Done:
    pMe->uLastError = (uint8_t)uErr;
 }
 
@@ -7337,19 +7261,14 @@
                                int64_t             *pnMantissa,
                                int64_t             *pnExponent)
 {
-   QCBORItem Item;
-   QCBORDecode_VGetNext(pMe, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DECIMAL_FRACTION, QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
-         QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
    QCBOR_Private_ProcessExpMantissa(pMe,
-                                    TagSpec,
+                                    uTagRequirement,
+                                    CBOR_TAG_DECIMAL_FRACTION,
+                                    uOffset,
                                    &Item,
                                     pnMantissa,
                                     pnExponent);
@@ -7367,21 +7286,17 @@
                                      int64_t             *pnExponent)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DECIMAL_FRACTION, QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
-         QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBOR_Private_ProcessExpMantissa(pMe,
-                                    TagSpec,
+                                    uTagRequirement,
+                                    CBOR_TAG_DECIMAL_FRACTION,
+                                    uOffset,
                                    &Item,
                                     pnMantissa,
                                     pnExponent);
+
 }
 
 
@@ -7395,19 +7310,14 @@
                                       int64_t             *pnMantissa,
                                       int64_t             *pnExponent)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DECIMAL_FRACTION, QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
-         QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBOR_Private_ProcessExpMantissa(pMe,
-                                    TagSpec,
+                                    uTagRequirement,
+                                    CBOR_TAG_DECIMAL_FRACTION,
+                                    uOffset,
                                    &Item,
                                     pnMantissa,
                                     pnExponent);
@@ -7425,19 +7335,14 @@
                                   bool               *pbMantissaIsNegative,
                                   int64_t            *pnExponent)
 {
-   QCBORItem Item;
-   QCBORDecode_VGetNext(pMe, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DECIMAL_FRACTION, QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
-         QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
    QCBORDecode_Private_ProcessExpMantissaBig(pMe,
-                                             TagSpec,
+                                             uTagRequirement,
+                                             CBOR_TAG_DECIMAL_FRACTION,
+                                             uOffset,
                                             &Item,
                                              MantissaBuffer,
                                              pMantissa,
@@ -7458,20 +7363,14 @@
                                         bool               *pbIsNegative,
                                         int64_t            *pnExponent)
 {
-
    QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DECIMAL_FRACTION, QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
-         QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBORDecode_Private_ProcessExpMantissaBig(pMe,
-                                             TagSpec,
+                                             uTagRequirement,
+                                             CBOR_TAG_DECIMAL_FRACTION,
+                                             uOffset,
                                             &Item,
                                              BufferForMantissa,
                                              pMantissa,
@@ -7493,18 +7392,13 @@
                                          int64_t            *pnExponent)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_DECIMAL_FRACTION, QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM,
-         QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBORDecode_Private_ProcessExpMantissaBig(pMe,
-                                             TagSpec,
+                                             uTagRequirement,
+                                             CBOR_TAG_DECIMAL_FRACTION,
+                                             uOffset,
                                             &Item,
                                              BufferForMantissa,
                                              pMantissa,
@@ -7523,18 +7417,13 @@
                         int64_t             *pnExponent)
 {
    QCBORItem Item;
-   QCBORDecode_VGetNext(pMe, &Item);
+   size_t    uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_BIGFLOAT, QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
-         QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
    QCBOR_Private_ProcessExpMantissa(pMe,
-                                    TagSpec,
+                                    uTagRequirement,
+                                    CBOR_TAG_BIGFLOAT,
+                                    uOffset,
                                    &Item,
                                     pnMantissa,
                                     pnExponent);
@@ -7552,18 +7441,13 @@
                               int64_t            *pnExponent)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_BIGFLOAT, QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
-         QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBOR_Private_ProcessExpMantissa(pMe,
-                                    TagSpec,
+                                    uTagRequirement,
+                                    CBOR_TAG_BIGFLOAT,
+                                    uOffset,
                                    &Item,
                                     pnMantissa,
                                     pnExponent);
@@ -7580,22 +7464,17 @@
                                int64_t            *pnMantissa,
                                int64_t            *pnExponent)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_BIGFLOAT, QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
-         QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBOR_Private_ProcessExpMantissa(pMe,
-                                    TagSpec,
-                                   &Item,
-                                    pnMantissa,
-                                    pnExponent);
+                                       uTagRequirement,
+                                       CBOR_TAG_BIGFLOAT,
+                                       uOffset,
+                                       &Item,
+                                       pnMantissa,
+                                       pnExponent);
 }
 
 
@@ -7610,19 +7489,14 @@
                            bool               *pbMantissaIsNegative,
                            int64_t            *pnExponent)
 {
-   QCBORItem Item;
-   QCBORDecode_VGetNext(pMe, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_BIGFLOAT, QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
-         QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
    QCBORDecode_Private_ProcessExpMantissaBig(pMe,
-                                             TagSpec,
+                                             uTagRequirement,
+                                             CBOR_TAG_BIGFLOAT,
+                                             uOffset,
                                             &Item,
                                              MantissaBuffer,
                                              pMantissa,
@@ -7643,24 +7517,19 @@
                                  bool               *pbIsNegative,
                                  int64_t            *pnExponent)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_BIGFLOAT, QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
-         QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBORDecode_Private_ProcessExpMantissaBig(pMe,
-                                             TagSpec,
-                                            &Item,
-                                             BufferForMantissa,
-                                             pMantissa,
-                                             pbIsNegative,
-                                             pnExponent);
+                                                uTagRequirement,
+                                                CBOR_TAG_BIGFLOAT,
+                                                uOffset,
+                                                &Item,
+                                                BufferForMantissa,
+                                                pMantissa,
+                                                pbIsNegative,
+                                                pnExponent);
 }
 
 
@@ -7677,23 +7546,18 @@
                                   int64_t            *pnExponent)
 {
    QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   size_t    uOffset;
 
-   const QCBOR_Private_TagSpec TagSpec =
-   {
-      uTagRequirement,
-      {QCBOR_TYPE_BIGFLOAT, QCBOR_TYPE_BIGFLOAT_POS_BIGNUM,
-         QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM, QCBOR_TYPE_NONE},
-      {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-   };
-
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
    QCBORDecode_Private_ProcessExpMantissaBig(pMe,
-                                             TagSpec,
-                                            &Item,
-                                             BufferForMantissa,
-                                             pMantissa,
-                                             pbIsNegative,
-                                             pnExponent);
+                                                uTagRequirement,
+                                                CBOR_TAG_BIGFLOAT,
+                                                uOffset,
+                                                &Item,
+                                                BufferForMantissa,
+                                                pMantissa,
+                                                pbIsNegative,
+                                                pnExponent);
 }
 
 #endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
@@ -7861,7 +7725,7 @@
    *pDest = *pSource + 1;
    while(1) {
       /* Wrap around from 0xff to 0 is a defined operation for
-	 unsigned addition in C. */
+       * unsigned addition in C.*/
       if(*pDest != 0) {
          /*  The add operation didn't wrap so no more carry. This
           * funciton only adds one, so when there is no more carry,
@@ -7907,6 +7771,7 @@
 }
 
 
+
 /*
  * Public function, see header qcbor/qcbor_decode.h
  */
@@ -7936,7 +7801,7 @@
    /* Compute required length so it can be returned if buffer is too small */
    switch(uType) {
       case QCBOR_TYPE_INT64:
-         uLen = QCBORDecode_Private_CountNonZeroBytes((uint64_t)(Item.val.int64 < 0 ? -Item.val.int64 : Item.val.int64));
+         uLen = QCBORDecode_Private_CountNonZeroBytes((uint64_t)ABSOLUTE_VALUE(Item.val.int64));
          break;
 
       case QCBOR_TYPE_UINT64:
@@ -8006,33 +7871,70 @@
 }
 
 
-static QCBORError
-QCBOR_Private_ProcessPreferredBigNum(const uint8_t    uTagRequirement,
-                                     const QCBORItem *pItem,
-                                     UsefulBuf        BigNumBuf,
-                                     UsefulBufC      *pValue,
-                                     bool            *pbIsNegative)
+
+
+static void
+QCBOR_Private_ProcessPreferredBigNum(QCBORDecodeContext *pMe,
+                                     const uint8_t       uTagRequirement,
+                                     QCBORItem          *pItem,
+                                     const size_t        uOffset,
+                                     UsefulBuf           BigNumBuf,
+                                     UsefulBufC         *pValue,
+                                     bool               *pbIsNegative)
 {
    if(pItem->uDataType != QCBOR_TYPE_INT64 &&
       pItem->uDataType != QCBOR_TYPE_UINT64 &&
       pItem->uDataType != QCBOR_TYPE_65BIT_NEG_INT) {
 
-      /* The integer types are always OK. If it's not an integer type drop
-       * in to the tag type checking system. */
-      const QCBOR_Private_TagSpec TagSpec =
-      {
-         uTagRequirement,
-         {QCBOR_TYPE_POSBIGNUM, QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
-         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
-      };
+      /* Two stage processing because big numbers are handled like that */
 
-      QCBORError uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
-      if(uErr != QCBOR_SUCCESS) {
-         return uErr;
+      const uint8_t puTypes[] = {QCBOR_TYPE_POSBIGNUM,QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE};
+      const uint64_t puTNs[] = {CBOR_TAG_POS_BIGNUM, CBOR_TAG_NEG_BIGNUM, CBOR_TAG_INVALID64};
+
+      QCBORDecode_Private_ProcessTagItemMulti(pMe,
+                                              pItem,
+                                              uTagRequirement,
+                                              puTypes,
+                                              puTNs,
+                                              QCBORDecode_StringsTagCB,
+                                              uOffset);
+
+      if(pMe->uLastError) {
+         return;
       }
    }
 
-   return QCBORDecode_BignumPreferred(*pItem, BigNumBuf, pValue, pbIsNegative);
+   pMe->uLastError = (uint8_t)QCBORDecode_BignumPreferred(*pItem, BigNumBuf, pValue, pbIsNegative);
+}
+
+
+static void
+QCBOR_Private_ProcessBigNumber(QCBORDecodeContext *pMe,
+                               const uint8_t       uTagRequirement,
+                               QCBORItem          *pItem,
+                               const size_t        uOffset,
+                               UsefulBuf           BigNumBuf,
+                               UsefulBufC         *pValue,
+                               bool               *pbIsNegative)
+{
+   /* Two stage processing because big numbers are handled like that */
+
+   const uint8_t puTypes[] = {QCBOR_TYPE_POSBIGNUM,QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE};
+   const uint64_t puTNs[] = {CBOR_TAG_POS_BIGNUM, CBOR_TAG_NEG_BIGNUM, CBOR_TAG_INVALID64};
+
+   QCBORDecode_Private_ProcessTagItemMulti(pMe,
+                                           pItem,
+                                           uTagRequirement,
+                                           puTypes,
+                                           puTNs,
+                                           QCBORDecode_StringsTagCB,
+                                           uOffset);
+
+   if(pMe->uLastError) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)QCBORDecode_BignumPreferred(*pItem, BigNumBuf, pValue, pbIsNegative);
 }
 
 
@@ -8047,19 +7949,32 @@
                                bool               *pbIsNegative)
 {
    QCBORItem Item;
+   size_t    uOffset;
 
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      /* Already in error state, do nothing */
-      return;
-   }
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBOR_Private_ProcessPreferredBigNum(pMe, uTagRequirement, &Item, uOffset, BigNumBuf, pValue, pbIsNegative);
+}
 
-   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
-   if(uError != QCBOR_SUCCESS) {
-      pMe->uLastError = (uint8_t)uError;
-      return;
-   }
 
-   QCBOR_Private_ProcessPreferredBigNum(uTagRequirement, &Item, BigNumBuf, pValue, pbIsNegative);
+
+void
+QCBORDecode_GetBigNumber(QCBORDecodeContext *pMe,
+                         const uint8_t       uTagRequirement,
+                         UsefulBuf           BigNumberBuf,
+                         UsefulBufC         *pBigNumber,
+                         bool               *pbIsNegative)
+{
+   QCBORItem  Item;
+   size_t     uOffset;
+
+   QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset);
+   QCBOR_Private_ProcessBigNumber(pMe,
+                                  uTagRequirement,
+                                  &Item,
+                                  uOffset,
+                                  BigNumberBuf,
+                                  pBigNumber,
+                                  pbIsNegative);
 }
 
 
@@ -8074,17 +7989,17 @@
                                      UsefulBufC         *pValue,
                                      bool               *pbIsNegative)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessPreferredBigNum(uTagRequirement,
-                                                                  &Item,
-                                                                   BigNumBuf,
-                                                                   pValue,
-                                                                   pbIsNegative);
+   QCBORDecode_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBOR_Private_ProcessPreferredBigNum(pMe,
+                                        uTagRequirement,
+                                       &Item,
+                                        uOffset,
+                                        BigNumBuf,
+                                        pValue,
+                                        pbIsNegative);
 }
 
 /*
@@ -8098,17 +8013,17 @@
                                       UsefulBufC         *pValue,
                                       bool               *pbIsNegative)
 {
-   QCBORItem Item;
-   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return;
-   }
+   QCBORItem  Item;
+   size_t     uOffset;
 
-   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessPreferredBigNum(uTagRequirement,
-                                                                  &Item,
-                                                                   BigNumBuf,
-                                                                   pValue,
-                                                                   pbIsNegative);
+   QCBORDecode_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset);
+   QCBOR_Private_ProcessPreferredBigNum(pMe,
+                                        uTagRequirement,
+                                       &Item,
+                                        uOffset,
+                                        BigNumBuf,
+                                        pValue,
+                                        pbIsNegative);
 }
 
-
+// TODO: re order above functions in tag number order