big change with the new tag decoding -- unlimited number of tags, better handling of custom tags
diff --git a/cmd_line_main.c b/cmd_line_main.c
index ee1180a..a01a0e0 100644
--- a/cmd_line_main.c
+++ b/cmd_line_main.c
@@ -51,6 +51,7 @@
     printf("sizeof(QCBORItem) %d\n", (uint32_t)sizeof(QCBORItem));
     printf("sizeof(QCBORStringAllocator) %d\n\n", (uint32_t)sizeof(QCBORStringAllocator));
 
+    // TODO: command line arg to select test
     int nNumTestsFailed = run_tests(&fputs_wrapper, stdout, NULL);
 
     return nNumTestsFailed;
diff --git a/inc/qcbor.h b/inc/qcbor.h
index d839db3..23e38fb 100644
--- a/inc/qcbor.h
+++ b/inc/qcbor.h
@@ -191,9 +191,14 @@
    // private part of this file and QCBORStringAllocat is defined
    // later in the public part of this file.
    void *pStringAllocator;
+   
+   // This is NULL or points to QCBORTagList.
+   // It is type void for the same reason as above.
+   const void *pCallerConfiguredTagList;
 };
 
 
+
 /* ===========================================================================
    END OF PRIVATE PART OF THIS FILE
 
@@ -270,6 +275,11 @@
 #define CBOR_TAG_NEG_BIGNUM     3
 #define CBOR_TAG_FRACTION       4
 #define CBOR_TAG_BIGFLOAT       5
+
+#define CBOR_TAG_COSE_ENCRYPTO 16
+#define CBOR_TAG_COSE_MAC0     17
+#define CBOR_TAG_COSE_SIGN1    18
+
 /* The data in byte string should be converted in base 64 URL when encoding in JSON or similar text-based representations */
 #define CBOR_TAG_ENC_AS_B64URL 21
 /* The data in byte string should be encoded in base 64 when encoding in JSON */
@@ -289,6 +299,16 @@
 #define CBOR_TAG_MIME          36
 /** Binary UUID */
 #define CBOR_TAG_BIN_UUID      37
+
+#define CBOR_TAG_CWT           61
+
+#define CBOR_TAG_ENCRYPT       96
+#define CBOR_TAG_MAC           97
+#define CBOR_TAG_SIGN          98
+
+#define CBOR_TAG_GEO_COORD    103
+
+
 /** The data is CBOR data */
 #define CBOR_TAG_CBOR_MAGIC 55799
 #define CBOR_TAG_NONE  UINT64_MAX
@@ -402,8 +422,8 @@
  
  Implementors using this API will primarily work with labels. Generally
  tags are only needed for making up new data types. This implementation
- covers most of the data types defined in the RFC using tags. However,
- it does allow for the creation of news tags if necessary.
+ covers most of the data types defined in the RFC using tags. It also,
+ allows for the creation of news tags if necessary.
  
  This implementation explicitly supports labels that are text strings
  and integers. Text strings translate nicely into JSON objects and
@@ -469,6 +489,9 @@
  Note that when you nest arrays or maps in a map, the nested
  array or map has a label.
  
+ TODO: describe decoding
+ 
+ 
  As mentioned callers of this API will generally not need tags
  and thus not need the "_3" functions, but they are available
  if need be. There is an IANA registry for new tags that are
@@ -476,14 +499,7 @@
  allowed for protocols to make up new tags in the range above 256.
  Note that even arrays and maps can be tagged.
  
- Tags in CBOR are a bit open-ended in particular allowing
- multiple tags per item, and the ability to tag deeply nested maps
- and arrays. Partly this is good as it allows them to be used 
- in lots of ways, but also makes a general purpose decoder 
- like this more difficult.
- 
- This implementation only supports one tag per data item
- during encoding and decoding.
+ TODO: describe tagging
   
  Summary Limits of this implementation:
  - The entire encoded CBOR must fit into contiguous memory.
@@ -528,6 +544,12 @@
 */
 #define QCBOR_MAX_ARRAY_NESTING  QCBOR_MAX_ARRAY_NESTING1
 
+/**
+ The maximum number of tags that can be in QCBORTagListIn and passed to
+ QCBORDecode_SetCallerConfiguredTagList()
+ */
+#define QCBOR_MAX_CUSTOM_TAGS    16
+
 
 
 
@@ -611,6 +633,9 @@
 /** The a break occurred outside an indefinite length item */
 #define QCBOR_ERR_BAD_BREAK               21
 
+/** Too many tags in the caller-configured tag list, or not enough space in QCBORTagListOut */
+#define QCBOR_ERR_TOO_MANY_TAGS           22
+
 
 /** See QCBORDecode_Init() */
 #define QCBOR_DECODE_MODE_NORMAL            0
@@ -699,6 +724,7 @@
       UsefulBufC  dateString; /** The value for uDataType QCBOR_TYPE_DATE_STRING */
       UsefulBufC  bigNum;     /** The value for uDataType QCBOR_TYPE_BIGNUM */
       uint8_t     uSimple;    /** The integer value for unknown simple types */
+      uint64_t    uTagV;
       
    } val;  /** The union holding the item's value. Select union member based on uDataType */
    
@@ -708,8 +734,7 @@
       uint64_t    uint64;  /** The label for uLabelType for QCBOR_TYPE_UINT64 */
    } label; /** Union holding the different label types selected based on uLabelType */
    
-   uint64_t uTag;     /** Any tag value that is greater than 63.  If there is more than one, then only the last one is recorded */
-   uint64_t uTagBits; /** Bits corresponding to tag values less than 63 as defined in RFC 7049, section 2.4 */
+   uint64_t uTagBits; /** Bit indicating which tags (major type 6) on this item.  */
    
 } QCBORItem;
 
@@ -743,25 +768,40 @@
 } QCBORStringAllocator;
 
 
-/** See the descriptions for CBOR_SIMPLEV_FALSE, CBOR_TAG_DATE_EPOCH... for
-    the meaning of the individual tags.  The values here are bit flags
-    associated with each tag.  These flags are set in uTagsBits in QCBORItem */
-#define QCBOR_TAGFLAG_DATE_STRING    (0x01LL << CBOR_TAG_DATE_STRING)
-#define QCBOR_TAGFLAG_DATE_EPOCH     (0x01LL << CBOR_TAG_DATE_EPOCH)
-#define QCBOR_TAGFLAG_POS_BIGNUM     (0x01LL << CBOR_TAG_POS_BIGNUM)
-#define QCBOR_TAGFLAG_NEG_BIGNUM     (0x01LL << CBOR_TAG_NEG_BIGNUM)
-#define QCBOR_TAGFLAG_FRACTION       (0x01LL << CBOR_TAG_FRACTION)
-#define QCBOR_TAGFLAG_BIGFLOAT       (0x01LL << CBOR_TAG_BIGFLOAT)
-#define QCBOR_TAGFLAG_ENC_AS_B64URL  (0x01LL << CBOR_TAG_ENC_AS_B64URL)
-#define QCBOR_TAGFLAG_ENC_AS_B64     (0x01LL << CBOR_TAG_ENC_AS_B64)
-#define QCBOR_TAGFLAG_ENC_AS_B16     (0x01LL << CBOR_TAG_ENC_AS_B16)
-#define QCBOR_TAGFLAG_CBOR           (0x01LL << CBOR_TAG_CBOR)
-#define QCBOR_TAGFLAG_URI            (0x01LL << CBOR_TAG_URI)
-#define QCBOR_TAGFLAG_B64URL         (0x01LL << CBOR_TAG_B64URL)
-#define QCBOR_TAGFLAG_B64            (0x01LL << CBOR_TAG_B64)
-#define QCBOR_TAGFLAG_REGEX          (0x01LL << CBOR_TAG_REGEX)
-#define QCBOR_TAGFLAG_MIME           (0x01LL << CBOR_TAG_MIME)
-#define QCBOR_TAGFLAG_CBOR_MAGIC     (0x01ULL << 63)
+
+/**
+ This is used to tell the decoder about tags that it should
+ record in uTagBits in QCBORItem beyond the built-in
+ tags. puTags points to an
+ array of uint64_t integers that are the tags. uNumTags
+ is the number of integers in the array. The maximum
+ size is QCBOR_MAX_CUSTOM_TAGS.  See QCBORDecode_IsTagged()
+ and QCBORDecode_SetCallerAddedTagMap().
+ */
+typedef struct {
+   uint8_t uNumTags;
+   const uint64_t *puTags;
+} QCBORTagListIn;
+
+
+/**
+ This is for QCBORDecode_GetNextWithTags() to be able to return the
+ full list of  tags on an item. It not needed for most CBOR protocol
+ implementations. Its primary use is for pretty-printing CBOR or
+ protocol conversion to another format.
+ 
+ On input, puTags points to a buffer to be filled in
+ and uNumAllocated is the number of uint64_t values
+ in the buffer.
+ 
+ On output the buffer contains the tags for the item.
+ uNumUsed tells how many there are.
+ */
+typedef struct {
+   uint8_t uNumUsed;
+   uint8_t uNumAllocated;
+   uint64_t *puTags;
+} QCBORTagListOut;
 
 
 /**
@@ -1635,7 +1675,7 @@
 
 /**
  QCBORDecodeContext is the data type that holds context decoding the
- data items for some received CBOR.  It is about 50 bytes so it can go
+ data items for some received CBOR.  It is about 100 bytes so it can go
  on the stack.  The contents are opaque and the caller should not
  access any internal items.  A context may be re used serially as long
  as it is re initialized.
@@ -1653,6 +1693,14 @@
  
  Initialize context for a pre-order traveral of the encoded CBOR tree.
  
+ Most CBOR decoding can be completed by calling this function to start
+ and QCBORDecode_GetNext() in a loop.  If indefinite length strings
+ are to be decoded, then QCBORDecode_SetMemPool() or QCBORDecode_SetUpAllocator()
+ must be called.  If tags other than built-in tags are to be
+ recognized, then QCBORDecode_SetCallerAddedTagMap() must be called.
+ The built-in tags are those for which a macro of the form
+ CBOR_TAG_XXX is defined.
+ 
  Three decoding modes are supported.  In normal mode, maps are decoded
  and strings and ints are accepted as map labels. If a label is other
  than these, the error QCBOR_ERR_MAP_LABEL_TYPE is returned by
@@ -1672,7 +1720,7 @@
 /**
  Set up the MemPool string allocator for indefinite length strings.
  
- @param[in] pCtx The decode context to initialize.
+ @param[in] pCtx The decode context.
  @param[in] MemPool The pointer and length of the memory pool.
  @param[in] bAllStrings true means to put even definite length strings in the pool.
  
@@ -1724,6 +1772,22 @@
 
 
 /**
+ @brief Configure list of caller selected tags to be recognized
+ 
+ @param[in] pCtx The decode context.
+ @param[out] pTagList Structure holding the list of tags to configure
+ 
+ This is used to tell the decoder about tags beyond those that are
+ built-in that should be recognized. The built-in tags are those
+ with macros of the form CBOR_TAG_XXX.
+ 
+ See description of QCBORTagListIn.
+ */
+
+void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *pCtx, const QCBORTagListIn *pTagList);
+
+
+/**
  @brief This returns a string allocator that uses malloc
  
  @return pointer to string allocator or NULL
@@ -1744,13 +1808,12 @@
 
 
 /**
- Gets the next item (integer, byte string, array...) in pre order traversal of CBOR tree
+ @brief Gets the next item (integer, byte string, array...) in pre order traversal of CBOR tree
  
  @param[in]  pCtx          The decoder context.
  @param[out] pDecodedItem  Holds the CBOR item just decoded.
  
- @return
- 0 or error.
+ @return 0 or error.
  
  pDecodedItem is filled in with the value parsed. Generally, the
  folloinwg data is returned in the structure.
@@ -1779,10 +1842,6 @@
  if Items in the array or map.  Typically an implementation will call
  QCBORDecode_GetNext() in a for loop to fetch them all.
  
- Optional tags are integer tags that are prepended to the actual data
- item. That tell more about the data. For example it can indicate data
- is a date or a big number or a URL.
- 
  Note that when traversing maps, the count is the number of pairs of
  items, so the for loop would decrement once for every two calls to
  QCBORDecode_GetNext().
@@ -1836,25 +1895,105 @@
  the end of the current map or array has been encountered. This
  works the same for both definite and indefinite length arrays.
  
+ Most uses of this decoder will not need to do anything extra for
+ tag handling. The built-in tags, those with a macro of the form
+ CBOR_TAG_XXXX, will be enough.
+
+ If tags beyond built-in tags are to be recognized, they must be
+ configured by calling QCBORDecode_SetCallerConfiguredTags(). If
+ a tag is not recognized it is silently ignored.
+ 
+ Several tagged types are automatically recognized and decoded and
+ returned in their decoded form.
+ 
+ To find ound if a QCBORItem was tagged with a particular tag
+ call QCBORDecode_IsTagged(). This works only for built-in
+ tags and caller-configured tags.
+ 
+ To get the full list of tags on an Item without having to
+ pre-configure any predetermined list of tags use
+ QCBORDecode_GetNextWithTags().
+ 
  */
 
 int QCBORDecode_GetNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
 
 
 /**
- Check whether all the bytes have been decoded
+ @brief Gets the next item including full list of tags for item
+ 
+ @param[in]  pCtx          The decoder context.
+ @param[out] pDecodedItem  Holds the CBOR item just decoded.
+ @param[in,out] pTagList   On input array to put tags in; on output the tags on this item.
+ 
+ @return 0 or error.
+ 
+ This works the same as QCBORDecode_GetNext() except that it also returns
+ the full list of tags for the data item. This function should only
+ be needed when parsing CBOR to print it out or convert it to some other
+ format. It should not be needed in an actual CBOR protocol implementation.
+ 
+ Tags will be returned here whether or not they are in the built-in or
+ caller-configured tag lists.
+ 
+ CBOR has no upper bound of limit on the number of tags that can be
+ associated with a data item. In practice the number of tags on an item
+ will usually be small, perhaps less than five. This will return an error
+ if the array in pTagList is too small to hold all the tags for an item.
+ 
+ (This function is separate from  QCBORDecode_GetNext() so as to not have to
+ make QCBORItem large enough to be able to hold a full list of tags. Even a list of
+ five tags would nearly double its size because tags can be a uint64_t).
+ 
+ */
+
+int QCBORDecode_GetNextWithTags(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem, QCBORTagListOut *pTagList);
+
+
+/**
+ @brief Determine if a CBOR item was tagged with a particular tag
+ 
+ @param[in] pCtx    The decoder context.
+ @param[in] pItem   The CBOR item to check
+ @param[in] uTag    The tag to check
+
+ @return 1 if it was tagged, 0 if not
+ 
+ QCBORDecode_GetNext() processes tags by looking them up
+ in two lists and setting a bit corresponding to the tag
+ in uTagBits in the QCBORItem. To find out if a
+ QCBORItem was tagged with a particular tag, call
+ this function. It handles the mapping between
+ the two lists of tags and the bits set for it.
+ 
+ The first tag list is the built-in tags, those
+ with a macro of the form CBOR_TAG_XXX in this
+ header file. There are up to 48 of these,
+ corresponding to the lower 48 tag bits.
+ 
+ The other optional tag list is the ones
+ the caller configured using QCBORDecode_SetCallerConfiguredTagList()
+ There are QCBOR_MAX_CUSTOM_TAGS (16) of these corresponding to the
+ upper 16 tag bits.
+ 
+ See also QCBORDecode_GetTags() and QCBORDecode_GetNextWithTags()
+ 
+ */
+int QCBORDecode_IsTagged(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint64_t uTag);
+
+
+/**
+ Check whether all the bytes have been decoded and maps and arrays closed.
  
  @param[in]  pCtx          The context to check
  
- @return QCBOR_ERR_EXTRA_BYTES or QCBOR_SUCCESS
+ @return QCBOR_SUCCESS or error
  
- This tells you if all the bytes give to QCBORDecode_Init() have
- been consumed or not. In most cases all bytes should be consumed
- in a correct parse. 
- 
- It is OK to call this multiple times during decoding and to call
- QCBORDecode_GetNext() after calling this. This only
- performs a check. It does not change the state of the decoder.
+ This tells you if all the bytes given to QCBORDecode_Init() have
+ been consumed and whether all maps and arrays were closed.
+ The decode is considered to be incorrect or incomplete if not
+ and an error will be returned.
+
  */
 
 int QCBORDecode_Finish(QCBORDecodeContext *pCtx);
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 831843c..b944eb6 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -204,6 +204,126 @@
 
 
 
+/*
+ This list of built-in tags. Only add tags here that are
+ clearly established and useful. Once a tag is added here
+ it can't be taken out as that would break backwards compatibility.
+ There are only 48 slots available forever.
+ */
+static const uint16_t spBuiltInTagMap[] = {
+   CBOR_TAG_DATE_STRING, // See TAG_MAPPER_FIRST_FOUR
+   CBOR_TAG_DATE_EPOCH, // See TAG_MAPPER_FIRST_FOUR
+   CBOR_TAG_POS_BIGNUM, // See TAG_MAPPER_FIRST_FOUR
+   CBOR_TAG_NEG_BIGNUM, // See TAG_MAPPER_FIRST_FOUR
+   CBOR_TAG_FRACTION,
+   CBOR_TAG_BIGFLOAT,
+   CBOR_TAG_COSE_ENCRYPTO,
+   CBOR_TAG_COSE_MAC0,
+   CBOR_TAG_COSE_SIGN1,
+   CBOR_TAG_ENC_AS_B64URL,
+   CBOR_TAG_ENC_AS_B64,
+   CBOR_TAG_ENC_AS_B16,
+   CBOR_TAG_CBOR,
+   CBOR_TAG_URI,
+   CBOR_TAG_B64URL,
+   CBOR_TAG_B64,
+   CBOR_TAG_REGEX,
+   CBOR_TAG_MIME,
+   CBOR_TAG_BIN_UUID,
+   CBOR_TAG_CWT,
+   CBOR_TAG_ENCRYPT,
+   CBOR_TAG_MAC,
+   CBOR_TAG_SIGN,
+   CBOR_TAG_GEO_COORD,
+   CBOR_TAG_CBOR_MAGIC
+};
+
+// This is used in a bit of cleverness in GetNext_TaggedItem() to
+// keep code size down and switch for the internal processing of
+// these types. This will break if the first four items in
+// spBuiltInTagMap don't have values 0,1,2,3. That is the
+// mapping is 0 to 0, 1 to 1, 2 to 2 and 3 to 3.
+#define QCBOR_TAGFLAG_DATE_STRING    (0x01LL << CBOR_TAG_DATE_STRING)
+#define QCBOR_TAGFLAG_DATE_EPOCH     (0x01LL << CBOR_TAG_DATE_EPOCH)
+#define QCBOR_TAGFLAG_POS_BIGNUM     (0x01LL << CBOR_TAG_POS_BIGNUM)
+#define QCBOR_TAGFLAG_NEG_BIGNUM     (0x01LL << CBOR_TAG_NEG_BIGNUM)
+
+#define TAG_MAPPER_FIRST_FOUR (QCBOR_TAGFLAG_DATE_STRING |\
+                               QCBOR_TAGFLAG_DATE_EPOCH  |\
+                               QCBOR_TAGFLAG_POS_BIGNUM  |\
+                               QCBOR_TAGFLAG_NEG_BIGNUM)
+
+#define TAG_MAPPER_TOTAL_TAG_BITS 64 // Number of bits in a uint64_t
+#define TAG_MAPPER_CUSTOM_TAGS_BASE_INDEX (TAG_MAPPER_TOTAL_TAG_BITS - QCBOR_MAX_CUSTOM_TAGS) // 48
+#define TAG_MAPPER_MAX_SIZE_BUILT_IN_TAGS (TAG_MAPPER_TOTAL_TAG_BITS - QCBOR_MAX_CUSTOM_TAGS ) // 48
+
+static inline int TagMapper_LookupBuiltIn(uint64_t uTag)
+{
+   if(sizeof(spBuiltInTagMap)/sizeof(uint16_t) > TAG_MAPPER_MAX_SIZE_BUILT_IN_TAGS) {
+      // This is a cross-check to make sure the above array doesn't
+      // accidentally get made too big.
+      // In normal conditions the above test should optimize out
+      // as all the values are known at compile time.
+      return -1;
+   }
+   
+   if(uTag > UINT16_MAX) {
+      // This tag map works only on 16-bit tags
+      return -1;
+   }
+   
+   for(int nTagBitIndex = 0; nTagBitIndex < (int)(sizeof(spBuiltInTagMap)/sizeof(uint16_t)); nTagBitIndex++) {
+      if(spBuiltInTagMap[nTagBitIndex] == uTag) {
+         return nTagBitIndex;
+      }
+   }
+   return -1; // Indicates no match
+}
+
+static inline int TagMapper_LookupCallerConfigured(const QCBORTagListIn *pCallerConfiguredTagMap, uint64_t uTag)
+{
+   for(int nTagBitIndex = 0; nTagBitIndex < pCallerConfiguredTagMap->uNumTags; nTagBitIndex++) {
+      if(pCallerConfiguredTagMap->puTags[nTagBitIndex] == uTag) {
+         return nTagBitIndex + TAG_MAPPER_CUSTOM_TAGS_BASE_INDEX;
+      }
+   }
+   
+   return -1; // Indicates no match
+}
+
+/*
+  Find the tag bit index for a given tag value, or error out
+ 
+ This and the above functions could probably be optimized and made
+ clearer and neater. 
+ */
+static int TagMapper_Lookup(const QCBORTagListIn *pCallerConfiguredTagMap, uint64_t uTag, uint8_t *puTagBitIndex)
+{
+   int nTagBitIndex = TagMapper_LookupBuiltIn(uTag);
+   if(nTagBitIndex >= 0) {
+      // Cast is safe because TagMapper_LookupBuiltIn never returns > 47
+      *puTagBitIndex = (uint8_t)nTagBitIndex;
+      return QCBOR_SUCCESS;
+   }
+   
+   if(pCallerConfiguredTagMap) {
+      if(pCallerConfiguredTagMap->uNumTags > QCBOR_MAX_CUSTOM_TAGS) {
+         return QCBOR_ERR_TOO_MANY_TAGS;
+      }
+      nTagBitIndex = TagMapper_LookupCallerConfigured(pCallerConfiguredTagMap, uTag);
+      if(nTagBitIndex >= 0) {
+         // Cast is safe because TagMapper_LookupBuiltIn never returns > 63
+
+         *puTagBitIndex = (uint8_t)nTagBitIndex;
+         return QCBOR_SUCCESS;
+      }
+   }
+   
+   return QCBOR_ERR_BAD_OPT_TAG;
+}
+
+
+
 
 /*
  Public function, see header file
@@ -224,10 +344,15 @@
  */
 void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator, bool bAllocAll)
 {
-    pCtx->pStringAllocator = (void *)pAllocator;
+   pCtx->pStringAllocator = (void *)pAllocator;
    pCtx->bStringAllocateAll = bAllocAll;
 }
 
+void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *me, const QCBORTagListIn *pTagList)
+{
+   me->pCallerConfiguredTagList = pTagList;
+}
+
 
 /*
  This decodes the fundamental part of a CBOR data item, the type and number
@@ -246,6 +371,7 @@
  */
 inline static int DecodeTypeAndNumber(UsefulInputBuf *pUInBuf, int *pnMajorType, uint64_t *puNumber, uint8_t *puAdditionalInfo)
 {
+   // Stack usage: int/ptr 5 -- 40
    int nReturn;
    
    // Get the initial byte that every CBOR data item has
@@ -323,6 +449,7 @@
  */
 inline static int DecodeInteger(int nMajorType, uint64_t uNumber, QCBORItem *pDecodedItem)
 {
+   // Stack usage: int/ptr 1 -- 8
    int nReturn = QCBOR_SUCCESS;
    
    if(nMajorType == CBOR_MAJOR_TYPE_POSITIVE_INT) {
@@ -385,6 +512,7 @@
 
 inline static int DecodeSimple(uint8_t uAdditionalInfo, uint64_t uNumber, QCBORItem *pDecodedItem)
 {
+   // Stack usage: 0
    int nReturn = QCBOR_SUCCESS;
    
    // uAdditionalInfo is 5 bits from the initial byte
@@ -443,6 +571,7 @@
  */
 inline static int DecodeBytes(const QCBORStringAllocator *pAlloc, int nMajorType, uint64_t uStrLen, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
 {
+   // Stack usage: UsefulBuf 2, int/ptr 1  40
    int nReturn = QCBOR_SUCCESS;
    
    UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(pUInBuf, uStrLen);
@@ -474,15 +603,16 @@
 /*
  Mostly just assign the right data type for the date string.
  */
-inline static int DecodeDateString(QCBORItem Item, QCBORItem *pDecodedItem)
+inline static int DecodeDateString(QCBORItem *pDecodedItem)
 {
-   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+   // Stack Use: UsefulBuf 1 16
+   if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
       return QCBOR_ERR_BAD_OPT_TAG;
    }
-   pDecodedItem->val.dateString = Item.val.string;
-   pDecodedItem->uDataType = QCBOR_TYPE_DATE_STRING;
-   pDecodedItem->uTagBits = Item.uTagBits;
-   pDecodedItem->uTag = Item.uTag;
+   
+   UsefulBufC Temp              = pDecodedItem->val.string;
+   pDecodedItem->val.dateString = Temp;
+   pDecodedItem->uDataType      = QCBOR_TYPE_DATE_STRING;
    return QCBOR_SUCCESS;
 }
 
@@ -490,15 +620,15 @@
 /*
  Mostly just assign the right data type for the bignum.
  */
-inline static int DecodeBigNum(QCBORItem Item, QCBORItem *pDecodedItem, uint64_t uTagFlags)
+inline static int DecodeBigNum(QCBORItem *pDecodedItem)
 {
-   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+   // Stack Use: UsefulBuf 1  -- 16
+   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
       return QCBOR_ERR_BAD_OPT_TAG;
    }
-   pDecodedItem->val.bigNum     = Item.val.string;
-   pDecodedItem->uDataType      = uTagFlags & QCBOR_TAGFLAG_POS_BIGNUM ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM;
-   pDecodedItem->uTagBits       = Item.uTagBits;
-   pDecodedItem->uTag           = Item.uTag;
+   UsefulBufC Temp          = pDecodedItem->val.string;
+   pDecodedItem->val.bigNum = Temp;
+   pDecodedItem->uDataType  = pDecodedItem->uTagBits & QCBOR_TAGFLAG_POS_BIGNUM ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM; // TODO: check this
    return QCBOR_SUCCESS;
 }
 
@@ -506,51 +636,46 @@
 /*
  The epoch formatted date. Turns lots of different forms of encoding date into uniform one
  */
-static int DecodeDateEpoch(QCBORItem Item, QCBORItem *pDecodedItem)
+static int DecodeDateEpoch(QCBORItem *pDecodedItem)
 {
+   // Stack usage: 1
    int nReturn = QCBOR_SUCCESS;
    
-   pDecodedItem->uTagBits                       = Item.uTagBits;
-   pDecodedItem->uTag                           = Item.uTag;
-   pDecodedItem->uDataType                      = QCBOR_TYPE_DATE_EPOCH;
    pDecodedItem->val.epochDate.fSecondsFraction = 0;
+   double d = pDecodedItem->val.dfnum; // Might not use this, but keeps code flow neater below
    
-   switch (Item.uDataType) {
+   switch (pDecodedItem->uDataType) {
          
       case QCBOR_TYPE_INT64:
-         pDecodedItem->val.epochDate.nSeconds = Item.val.int64;
+         pDecodedItem->val.epochDate.nSeconds = pDecodedItem->val.int64;
          break;
          
       case QCBOR_TYPE_UINT64:
-         if(Item.val.uint64 > INT64_MAX) {
-            nReturn = QCBOR_ERR_DATE_OVERFLOW; 
+         if(pDecodedItem->val.uint64 > INT64_MAX) {
+            nReturn = QCBOR_ERR_DATE_OVERFLOW;
             goto Done;
          }
-         pDecodedItem->val.epochDate.nSeconds = Item.val.uint64;
+         pDecodedItem->val.epochDate.nSeconds = pDecodedItem->val.uint64;
          break;
          
       case QCBOR_TYPE_FLOAT:
-         // TODO: can we save code by widening a float to a double here? Then drop into double-handling code
-         if(Item.val.fnum > INT64_MAX) {
-            nReturn = QCBOR_ERR_DATE_OVERFLOW;
-            goto Done;
-         }
-         pDecodedItem->val.epochDate.nSeconds = Item.val.fnum;
-         pDecodedItem->val.epochDate.fSecondsFraction = Item.val.fnum - pDecodedItem->val.epochDate.nSeconds;
-         break;
-
+         d = pDecodedItem->val.fnum;
+         // Fall through
+         
       case QCBOR_TYPE_DOUBLE:
-         if(Item.val.dfnum > INT64_MAX) {
+         if(d > INT64_MAX) {
             nReturn = QCBOR_ERR_DATE_OVERFLOW;
             goto Done;
          }
-         pDecodedItem->val.epochDate.nSeconds = Item.val.dfnum;
-         pDecodedItem->val.epochDate.fSecondsFraction = Item.val.dfnum - pDecodedItem->val.epochDate.nSeconds;
+         pDecodedItem->val.epochDate.nSeconds = d; // Float to integer conversion happening here.
+         pDecodedItem->val.epochDate.fSecondsFraction = d - pDecodedItem->val.epochDate.nSeconds;
          break;
          
       default:
          nReturn = QCBOR_ERR_BAD_OPT_TAG;
+         goto Done;
    }
+   pDecodedItem->uDataType = QCBOR_TYPE_DATE_EPOCH;
    
 Done:
    return nReturn;
@@ -577,6 +702,7 @@
  */
 static int GetNext_Item(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, const QCBORStringAllocator *pAlloc)
 {
+   // Stack usage: int/ptr 3 -- 24
    int nReturn;
    
    // Get the major type and the number. Number could be length of more bytes or the value depending on the major type
@@ -628,7 +754,7 @@
          break;
          
       case CBOR_MAJOR_TYPE_OPTIONAL: // Major type 6, optional prepended tags
-         pDecodedItem->uTag      = uNumber;
+         pDecodedItem->val.uTagV = uNumber;
          pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
          break;
          
@@ -654,8 +780,9 @@
  
  Code Reviewers: THIS FUNCTION DOES A LITTLE POINTER MATH
  */
-static int GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+static inline int GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
 {
+   // Stack usage; int/ptr 2 UsefulBuf 2 QCBORItem  -- 96
    int nReturn;
    QCBORStringAllocator *pAlloc = (QCBORStringAllocator *)me->pStringAllocator;
    UsefulBufC FullString = NULLUsefulBufC;
@@ -692,7 +819,7 @@
    // Loop getting segments of indefinite string
    for(;;) {
       // Get item for next segment
-      QCBORItem StringSegmentItem;
+      QCBORItem StringSegmentItem; // TODO: rename segment to chunk to line up with new RFC
       // NULL passed to never string alloc segments of indefinite length strings
       nReturn = GetNext_Item(&(me->InBuf), &StringSegmentItem, NULL);
       if(nReturn) {
@@ -742,86 +869,74 @@
  Returns an error if there was something wrong with the optional item or it couldn't
  be handled.
  */
-static int GetNext_TaggedItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+static int GetNext_TaggedItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem, QCBORTagListOut *pTags)
 {
-   int nReturn;
-   
-   // TODO: optimize loop below so there is only one call to GetNext_FullItem?
-   nReturn = GetNext_FullItem(me, pDecodedItem);
-   if(nReturn) {
-      goto Done;
+   // Stack usage: int/ptr: 3 -- 24
+   int       nReturn;
+   uint64_t  uTagBits = 0;
+   if(pTags) {
+      pTags->uNumUsed = 0;
    }
-   
-   if(pDecodedItem->uDataType != QCBOR_TYPE_OPTTAG) {
-      goto Done;
-   }
-   
-   uint64_t uTagFlags = 0; // accumulate the tags in the form of flags
-   uint64_t uTagToProcess = pDecodedItem->uTag; // First process tag passed in
-   
-   QCBORItem Item;
-   
-   do {
-      if(uTagToProcess < 63) { // 63 is the number of bits in a uint64 - 1
-         uTagFlags |= 0x01LL << uTagToProcess;
-      } else if(uTagToProcess == CBOR_TAG_CBOR_MAGIC) {
-         uTagFlags |= QCBOR_TAGFLAG_CBOR_MAGIC;
-      }
-      /* This code ignores the all but the first tag of value
-       greater than 63. Ignoring tags that are not understoof
-       is allowed by the standard. Multiple tags are
-       presumably rare. */
-      
-      nReturn = GetNext_FullItem(me, &Item);
+
+   for(;;) {
+      nReturn = GetNext_FullItem(me, pDecodedItem);
       if(nReturn) {
-         // Bail out of the whole item fetch on any sort of error here
-         goto Done;
+         goto Done; // Error out of the loop
       }
       
-      if(Item.uDataType != QCBOR_TYPE_OPTTAG) {
+      if(pDecodedItem->uDataType != QCBOR_TYPE_OPTTAG) {
+         // Successful exit from loop; maybe got some tags, maybe not
+         pDecodedItem->uTagBits = uTagBits;
          break;
       }
+   
+      uint8_t uTagBitIndex;
+      // Tag was mapped, tag was not mapped, error with tag list
+      switch(TagMapper_Lookup(me->pCallerConfiguredTagList, pDecodedItem->val.uTagV, &uTagBitIndex)) {
+            
+         case QCBOR_SUCCESS:
+            // Successfully mapped the tag
+            uTagBits |= 0x01ULL << uTagBitIndex;
+            break;
+            
+         case QCBOR_ERR_BAD_OPT_TAG:
+            // Tag is not recognized. Do nothing
+            break;
+            
+         default:
+            // Error Condition
+            goto Done;
+      }
       
-      uTagToProcess = Item.uTag;
-   } while (1);
+      if(pTags) {
+         // Caller wants all tags recorded in the provided buffer
+         if(pTags->uNumUsed >= pTags->uNumAllocated) {
+            nReturn = QCBOR_ERR_TOO_MANY_TAGS;
+            goto Done;
+         }
+         pTags->puTags[pTags->uNumUsed] = pDecodedItem->val.uTagV;
+         pTags->uNumUsed++;
+      }
+   }
    
-   
-   /*
-    CBOR allows multiple tags on a data item. It also defines
-    a number of standard tag values, most of which are
-    less than 64.  This code can deal with multiple tag
-    values that are less than 64 and the last tag of multiple
-    if the value is more than 64. Or said another way
-    if there is one tag with a value >64 this code works.
-    
-    The assumption is that multiple tag values > 64 are rare.
-    
-    At this point in this code. uTagFlags has all the flags
-    < 64 and uTagToProcess has the last tag.
-    
-    Does this deal with multiple tags on an item we process?
-    */
-   
-   Item.uTagBits = uTagFlags;
-   Item.uTag = uTagToProcess;
-   
-   switch(uTagFlags & (QCBOR_TAGFLAG_DATE_STRING | QCBOR_TAGFLAG_DATE_EPOCH | QCBOR_TAGFLAG_POS_BIGNUM |QCBOR_TAGFLAG_NEG_BIGNUM)) {
+   switch(pDecodedItem->uTagBits & TAG_MAPPER_FIRST_FOUR) {
       case 0:
-         // No tags we know about. Pass them up
-         *pDecodedItem = Item;
+         // No tags at all or none we know about. Nothing to do.
+         // This is part of the pass-through path of this function
+         // that will mostly be taken when decoding any item.
          break;
          
       case QCBOR_TAGFLAG_DATE_STRING:
-         nReturn = DecodeDateString(Item, pDecodedItem);
+         nReturn = DecodeDateString(pDecodedItem);
          break;
          
       case QCBOR_TAGFLAG_DATE_EPOCH:
-         nReturn = DecodeDateEpoch(Item, pDecodedItem);
+         nReturn = DecodeDateEpoch(pDecodedItem);
          break;
          
       case QCBOR_TAGFLAG_POS_BIGNUM:
       case QCBOR_TAGFLAG_NEG_BIGNUM:
-         nReturn = DecodeBigNum(Item, pDecodedItem, uTagFlags);
+         nReturn = DecodeBigNum(pDecodedItem);
          break;
          
       default:
@@ -838,13 +953,15 @@
 /*
  This layer takes care of map entries. It combines the label and data items into one QCBORItem.
  */
-static inline int GetNext_MapEntry(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+static inline int GetNext_MapEntry(QCBORDecodeContext *me, QCBORItem *pDecodedItem, QCBORTagListOut *pTags)
 {
-   int nReturn = GetNext_TaggedItem(me, pDecodedItem);
+   // Stack use: int/ptr 1, QCBORItem  -- 56
+   int nReturn = GetNext_TaggedItem(me, pDecodedItem, pTags);
    if(nReturn)
       goto Done;
    
    if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+      // Break can't be a map entry
       goto Done;
    }
    
@@ -854,7 +971,7 @@
       
       // Get the next item which will be the real data; Item will be the label
       QCBORItem LabelItem = *pDecodedItem;
-      nReturn = GetNext_TaggedItem(me, pDecodedItem);
+      nReturn = GetNext_TaggedItem(me, pDecodedItem, pTags);
       if(nReturn)
          goto Done;
       
@@ -881,6 +998,7 @@
       } else {
          // label is not an int or a string. It is an arrray
          // or a float or such and this implementation doesn't handle that.
+         // Also, tags on labels are ignored.
          nReturn = QCBOR_ERR_MAP_LABEL_TYPE ;
          goto Done;
       }
@@ -894,13 +1012,15 @@
 /*
  Public function, see header qcbor.h file
  */
-int QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+int QCBORDecode_GetNextWithTags(QCBORDecodeContext *me, QCBORItem *pDecodedItem, QCBORTagListOut *pTags)
 {
+   // Stack ptr/int: 2, QCBORItem : 64
+
    // The public entry point for fetching and parsing the next QCBORItem.
    // All the CBOR parsing work is here and in subordinate calls.
    int nReturn;
    
-   nReturn = GetNext_MapEntry(me, pDecodedItem);
+   nReturn = GetNext_MapEntry(me, pDecodedItem, pTags);
    if(nReturn) {
       goto Done;
    }
@@ -974,6 +1094,12 @@
 }
 
 
+int QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+   return QCBORDecode_GetNextWithTags(me, pDecodedItem, NULL);
+}
+
+
 /*
  Decoding items is done in 5 layered functions, one calling the
  next one down. If a layer has no work to do for a particular item
@@ -1003,12 +1129,34 @@
  item in CBOR, the thing with an initial byte containing
  the major type.
  
+ Roughly this takes 300 bytes of stack for vars. Need to
+ evaluate this more carefully and correctly.
+ 
  */
 
 
 /*
  Public function, see header qcbor.h file
  */
+int QCBORDecode_IsTagged(QCBORDecodeContext *me, const QCBORItem *pItem, uint64_t uTag)
+{
+   const QCBORTagListIn *pCallerConfiguredTagMap = me->pCallerConfiguredTagList;
+   
+   uint8_t uTagBitIndex;
+   // Do not care about errors in pCallerConfiguredTagMap here. They are
+   // caught during GetNext() before this is called.
+   if(TagMapper_Lookup(pCallerConfiguredTagMap, uTag, &uTagBitIndex)) {
+      return 0;
+   }
+   
+   const uint64_t uTagBit = 0x01ULL << uTagBitIndex;
+   return (uTagBit & pItem->uTagBits) != 0;
+}
+
+
+/*
+ Public function, see header qcbor.h file
+ */
 int QCBORDecode_Finish(QCBORDecodeContext *me)
 {
    int nReturn = QCBOR_SUCCESS;
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 9c25468..c6ab0ea 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -1299,7 +1299,8 @@
    0xc1, // tag for epoch date
    0x1a, 0x53, 0x72, 0x4E, 0x00, // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
 
-   0xc1, 0xcf, 0xd8, 0xee, // Epoch date with extra tags
+   // CBOR_TAG_B64
+   0xc1, 0xcf, 0xd8, 0x22, // 0xee, // Epoch date with extra tags TODO: fix this test
    0x1a, 0x53, 0x72, 0x4E, 0x01,
 
    0xc1, // tag for epoch date
@@ -1333,6 +1334,11 @@
    
    QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(s_DateTestInput), QCBOR_DECODE_MODE_NORMAL);
    
+   const uint64_t uTags[] = {15};
+   QCBORTagListIn TagList = {1, uTags};
+      
+   QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TagList);
+   
    // String date
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
       return -1;
@@ -1350,14 +1356,14 @@
       return -1;
    }
    
-   // Epoch date with extra tags
+   // Epoch date with extra CBOR_TAG_B64 tag that doesn't really mean anything
+   // but want to be sure extra tag doesn't cause a problem
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
       return -1;
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1400000001 ||
       Item.val.epochDate.fSecondsFraction != 0 ||
-      Item.uTagBits != (0x02 | (0x01 << 0x0f)) ||
-      Item.uTag != 0xee) {
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_B64)) {
       return -1;
    }
    
@@ -1385,35 +1391,335 @@
    return 0;
 }
 
+// Really simple basic input for tagging test
 static uint8_t s_OptTestInput[] = {
    0xd9, 0xd9, 0xf7, // CBOR magic number
-   0x81,
-   0xd8, 62, // 62 is decimal intentionally
-   0x00};
+   0x81, // Array of one
+   0xd8, 0x04, // non-preferred serialization of tag 4
+   0x82, 0x01, 0x03}; // fraction 1/3
+
+static uint8_t spEncodedLargeTag[] = {0xdb, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x80};
+
+// 0x9192939495969798, 0x88, 0x01, 0x04
+static uint8_t spLotsOfTags[] = {0xdb, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xd8, 0x88, 0xc5, 0xc4, 0x80};
+
+/*
+ The cbor.me parse of this.
+ 55799(55799(55799({6(7(-23)): 5859837686836516696(7({7(-20): 11({17(-18): 17(17(17("Organization"))),
+ 9(-17): 773("SSG"), -15: 4(5(6(7(8(9(10(11(12(13(14(15("Confusion")))))))))))), 17(-16): 17("San Diego"),
+ 17(-14): 17("US")}), 23(-19): 19({-11: 9({-9: -7}),
+ 90599561(90599561(90599561(-10))): 12(h'0102030405060708090A')})})),
+ 16(-22): 23({11(8(7(-5))): 8(-3)})})))
+ */
+static uint8_t spCSRWithTags[] = {
+   0xd9, 0xd9, 0xf7, 0xd9, 0xd9, 0xf7, 0xd9, 0xd9, 0xf7, 0xa2,
+      0xc6, 0xc7, 0x36,
+      0xdb, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0xc7, 0xa2,
+         0xda, 0x00, 0x00, 0x00, 0x07, 0x33,
+         0xcb, 0xa5,
+            0xd1, 0x31,
+            0xd1, 0xd1, 0xd1, 0x6c,
+               0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+            0xc9, 0x30,
+            0xd9, 0x03, 0x05, 0x63,
+               0x53, 0x53, 0x47,
+            0x2e,
+            0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0x69,
+               0x43, 0x6f, 0x6e, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e,
+            0xd1, 0x2f,
+            0xd1, 0x69,
+               0x53, 0x61, 0x6e, 0x20, 0x44, 0x69, 0x65, 0x67, 0x6f,
+            0xd1, 0x2d,
+            0xd1, 0x62,
+               0x55, 0x53,
+         0xd7, 0x32,
+         0xd3, 0xa2,
+            0x2a,
+            0xc9, 0xa1,
+               0x28,
+               0x26,
+            0xda, 0x05, 0x66, 0x70, 0x89, 0xda, 0x05, 0x66, 0x70, 0x89, 0xda, 0x05, 0x66, 0x70, 0x89, 0x29,
+            0xcc, 0x4a,
+               0x01, 0x02, 0x03, 0x04, 0x05, 0x06,0x07, 0x08, 0x09, 0x0a,
+   0xd0, 0x35,
+   0xd7, 0xa1,
+      0xcb, 0xc8, 0xc7, 0x24,
+      0xc8, 0x22};
+
+static int CheckCSRMaps(QCBORDecodeContext *pDC);
+
 
 int OptTagParseTest()
 {
    QCBORDecodeContext DCtx;
    QCBORItem Item;
-   int nCBORError;
-   
    
    QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(s_OptTestInput), QCBOR_DECODE_MODE_NORMAL);
    
-   //
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+   //-------------------------
+   // This text matches the magic number tag and the fraction tag
+   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+      return -2;
+   }
    if(Item.uDataType != QCBOR_TYPE_ARRAY ||
-      Item.uTagBits != QCBOR_TAGFLAG_CBOR_MAGIC) {
-      return -1;
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_CBOR_MAGIC)) {
+      return -3;
    }
    
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+      return -4;
+   }
+   if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_FRACTION) ||
+      Item.val.uCount != 2) {
+      return -5;
+   }
+   
+   // --------------------------------
+   // This test decodes the very large tag, but it is not in
+   // any list so it is ignored.
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag), QCBOR_DECODE_MODE_NORMAL);
+   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+      return -6;
+   }
+   if(Item.uTagBits) {
+      return -7;
+   }
+   
+   // ----------------------------------
+   // This test sets up a caller-config list that includes the very large tage and then matches it.
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag), QCBOR_DECODE_MODE_NORMAL);
+   const uint64_t puList[] = {0x9192939495969798, 257};
+   const QCBORTagListIn TL = {2, puList};
+   QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TL);
+   
+   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+      return -8;
+   }
+   if(Item.uDataType != QCBOR_TYPE_ARRAY ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 0x9192939495969798) ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 257) ||
+      QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_BIGFLOAT) ||
+      Item.val.uCount != 0) {
+      return -9;
+   }
+   
+   //------------------------
+   // This test sets up a caller-configured list, and looks up something not in it
+   const uint64_t puLongList[17] = {1,2,1};
+   const QCBORTagListIn TLLong = {17, puLongList};
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag), QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TLLong);
+   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+      return -11;
+   }
+   
+   // -----------------------
+   // This tests retrievel of the full tag list
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLotsOfTags), QCBOR_DECODE_MODE_NORMAL);
+   uint64_t puTags[16];
+   QCBORTagListOut Out = {0, 4, puTags};
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -12;
+   }
+   if(puTags[0] != 0x9192939495969798 ||
+      puTags[1] != 0x88 ||
+      puTags[2] != 0x05 ||
+      puTags[3] != 0x04) {
+      return -13;
+   }
+   
+   // ----------------------
+   // This text if too small of an out list
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLotsOfTags), QCBOR_DECODE_MODE_NORMAL);
+   QCBORTagListOut OutSmall = {0, 3, puTags};
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &OutSmall) != QCBOR_ERR_TOO_MANY_TAGS) {
+      return -14;
+   }
+   
+   // ---------------
+   // Parse a version of the "CSR" that has had a ton of tags randomly inserted
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRWithTags), QCBOR_DECODE_MODE_NORMAL);
+   int n = CheckCSRMaps(&DCtx);
+   if(n) {
+      return n-2000;
+   }
+   
+   Out = (QCBORTagListOut){0,16, puTags};
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRWithTags), QCBOR_DECODE_MODE_NORMAL);
+   
+   const uint64_t puTagList[] = {773, 1, 90599561};
+   const QCBORTagListIn TagList = {3, puTagList};
+   QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TagList);
+   
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -100;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_CBOR_MAGIC) ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 90599561) ||
+      QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_DATE_EPOCH) ||
+      Item.val.uCount != 2 ||
+      puTags[0] != CBOR_TAG_CBOR_MAGIC ||
+      puTags[1] != CBOR_TAG_CBOR_MAGIC ||
+      puTags[2] != CBOR_TAG_CBOR_MAGIC ||
+      Out.uNumUsed != 3) {
+      return -101;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -102;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP ||
+      QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_CBOR_MAGIC) ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 6) ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 7) || // item is tagged 7, but 7 is not configured to be recognized
+      Item.val.uCount != 2 ||
+      puTags[0] != 5859837686836516696 ||
+      puTags[1] != 7 ||
+      Out.uNumUsed != 2) {
+      return -103;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -104;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP ||
+      Item.uTagBits ||
+      Item.val.uCount != 5 ||
+      puTags[0] != 0x0b ||
+      Out.uNumUsed != 1) {
+      return -105;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -106;
+   }
+   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_COSE_MAC0) ||
+      Item.val.string.len != 12 ||
+      puTags[0] != CBOR_TAG_COSE_MAC0 ||
+      puTags[1] != CBOR_TAG_COSE_MAC0 ||
+      puTags[2] != CBOR_TAG_COSE_MAC0 ||
+      Out.uNumUsed != 3) {
+      return -105;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -107;
+   }
+   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 773) ||
+      Item.val.string.len != 3 ||
+      puTags[0] != 773 ||
+      Out.uNumUsed != 1) {
+      return -108;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -109;
+   }
+   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 4) ||
+      Item.val.string.len != 9 ||
+      puTags[0] != 4 ||
+      puTags[11] != 0x0f ||
+      Out.uNumUsed != 12) {
+      return -110;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -111;
+   }
+   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 17) ||
+      Item.val.string.len != 9 ||
+      puTags[0] != 17 ||
+      Out.uNumUsed != 1) {
+      return -112;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -111;
+   }
+   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 17) ||
+      Item.val.string.len != 2 ||
+      puTags[0] != 17 ||
+      Out.uNumUsed != 1) {
+      return -112;
+   }
+
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -113;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 19) ||
+      Item.val.uCount != 2 ||
+      puTags[0] != 19 ||
+      Out.uNumUsed != 1) {
+      return -114;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -115;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 9) ||
+      Item.uTagBits ||
+      Item.val.uCount != 1 ||
+      puTags[0] != 9 ||
+      Out.uNumUsed != 1) {
+      return -116;
+   }
+
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -116;
+   }
    if(Item.uDataType != QCBOR_TYPE_INT64 ||
-      Item.uTagBits != (0x01LL << 62) ||
-      Item.val.int64 != 0)
-      return -1;
+      Item.val.int64 != -7 ||
+      Item.uTagBits ||
+      Out.uNumUsed != 0) {
+      return -117;
+   }
+
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -118;
+   }
+   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
+      Item.val.string.len != 10 ||
+      Item.uTagBits ||
+      puTags[0] != 12 ||
+      Out.uNumUsed != 1) {
+      return -119;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -120;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_ENC_AS_B16) ||
+      Item.val.uCount != 1 ||
+      puTags[0] != 0x17 ||
+      Out.uNumUsed != 1) {
+      return -121;
+   }
+   
+   if(QCBORDecode_GetNextWithTags(&DCtx, &Item, &Out)) {
+      return -122;
+   }
+   if(Item.uDataType != QCBOR_TYPE_INT64 ||
+      QCBORDecode_IsTagged(&DCtx, &Item, 8) ||
+      Item.val.int64 != -3 ||
+      puTags[0] != 8 ||
+      Out.uNumUsed != 1) {
+      return -123;
+   }
+   
+   if(QCBORDecode_Finish(&DCtx)) {
+      return -124;
+   }
    
    return 0;
 }
@@ -1712,7 +2018,7 @@
 static const uint8_t pIndefiniteArrayBad2[] = {0x9f, 0x9f, 0x02, 0xff}; // Not enough closing breaks
 static const uint8_t pIndefiniteArrayBad3[] = {0x9f, 0x02, 0xff, 0xff}; // Too many closing breaks
 static const uint8_t pIndefiniteArrayBad4[] = {0x81, 0x9f}; // Unclosed indeflen inside def len
-static const uint8_t pIndefiniteArrayBad5[] = {0x9f, 0xc7, 0xff}; // confused tag
+static const uint8_t pIndefiniteArrayBad5[] = {0x9f, 0xd1, 0xff}; // confused tag
 
 int IndefiniteLengthArrayMapTest()
 {