Spiffy decode -- major new decode features

Spiffy decode is an additional decoding API layered on top of the previous. It supports searching maps by label and duplicate label detection. It supports fetching items by type. It has internal error tracking. It is easier to use and code for decoding a protocol parallels the code for encoding a protocol.

While many new encode functions are added for tag encoding, no changes were made to the core of the decoder.

Introduces the QCBORDecode_GetXxxx functions to get a data item of a specific type with internal error tracking. Allows writing a CBOR protocol decoder with many times fewer lines of code. The protocol decoder mirrors the protocol encoder and is easier to read.

Map searching by label with duplicate detection and type matching is added.

Ability to enter and exit arrays and maps as a clean way to decode these. The same code will work for definite length and indefinite length maps and arrays.

Ability to decode many different number formats with conversion. For example, decimal fractions and big numbers can be fetched and converted into doubles. This however brings in a lot of object code.

A pretty example is added showing various ways of decoding a map.

Ability to encode floats and doubles with or without preferred encoding (conversion to half-precision or single-precision)

Handling of tags is revised to much better align with the intended CBOR design (this design is much more clear in the CBORbis document than in RFC 7049). The new design is simpler to use, though it has a hard limit of four levels of tag nesting.

New methods for UsefulInputBuf to manipulate the input buffer length.

Reorganized error codes so they are 1) separate from decode and encode, 2) not well-formed are contiguous so it is easy to check for them 3) unrecoverable decode errors are together so it is easy to check for them

Added the newly defined standard CBOR invalid tags.

Full support for decoding of all tags described in RFC 7049. (only encoding was previously supported).

Added QCBORDecode_VGetNext() which is QCBORDecode_GetNext with spiffy decode style error handling.

Fixes bugs when decoding combinations of definite and indefinite length maps and arrays.

Uses tag 257 to encode MIME messages rather than 36. This is a non-backwards compatible change.
diff --git a/Makefile b/Makefile
index 543342f..1f9b580 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@
 CC=cc
 #CC=/usr/local/bin/gcc-9
 
-CFLAGS=-I inc -I test -Os -fPIC
+CFLAGS=$(CMD_LINE) -I inc -I test -Os -fPIC 
 
 # The following are used before a release of QCBOR help to make sure
 # the code compiles and runs in the most strict environments, but not
@@ -22,20 +22,17 @@
 
 TEST_OBJ=test/UsefulBuf_Tests.o test/qcbor_encode_tests.o \
     test/qcbor_decode_tests.o test/run_tests.o \
-    test/float_tests.o test/half_to_double_from_rfc7049.o
+    test/float_tests.o test/half_to_double_from_rfc7049.o example.o
 
 .PHONY: all so install uninstall clean
 
-all: qcbortest qcbormin libqcbor.a
+all: qcbortest libqcbor.a
 
 so:	libqcbor.so
 
 qcbortest: libqcbor.a $(TEST_OBJ) cmd_line_main.o
 	$(CC) -o $@ $^  libqcbor.a
 
-qcbormin: libqcbor.a min_use_main.o
-	$(CC) -dead_strip -o $@ $^ libqcbor.a
-
 libqcbor.a: $(QCBOR_OBJ)
 	ar -r $@ $^
 
@@ -45,15 +42,17 @@
 libqcbor.so: $(QCBOR_OBJ)
 	$(CC) -shared $^ $(CFLAGS) -o $@
 
-PUBLIC_INTERFACE=inc/qcbor/UsefulBuf.h inc/qcbor/qcbor_private.h inc/qcbor/qcbor_common.h inc/qcbor/qcbor_encode.h inc/qcbor/qcbor_decode.h
+PUBLIC_INTERFACE=inc/qcbor/UsefulBuf.h inc/qcbor/qcbor_private.h inc/qcbor/qcbor_common.h inc/qcbor/qcbor_encode.h inc/qcbor/qcbor_decode.h inc/qcbor/qcbor_spiffy_decode.h
 
 src/UsefulBuf.o: inc/qcbor/UsefulBuf.h
-src/qcbor_decode.o: inc/qcbor/UsefulBuf.h inc/qcbor/qcbor_private.h inc/qcbor/qcbor_common.h inc/qcbor/qcbor_encode.h src/ieee754.h
-src/qcbor_encode.o: inc/qcbor/UsefulBuf.h inc/qcbor/qcbor_private.h inc/qcbor/qcbor_common.h inc/qcbor/qcbor_decode.h src/ieee754.h
+src/qcbor_decode.o: inc/qcbor/UsefulBuf.h inc/qcbor/qcbor_private.h inc/qcbor/qcbor_common.h inc/qcbor/qcbor_decode.h inc/qcbor/qcbor_spiffy_decode.h src/ieee754.h
+src/qcbor_encode.o: inc/qcbor/UsefulBuf.h inc/qcbor/qcbor_private.h inc/qcbor/qcbor_common.h inc/qcbor/qcbor_encode.h src/ieee754.h
 src/iee754.o: src/ieee754.h
 src/qcbor_err_to_str.o: inc/qcbor/qcbor_common.h
 
-test/run_tests.o: test/UsefulBuf_Tests.h test/float_tests.h test/run_tests.h test/qcbor_encode_tests.h test/qcbor_decode_tests.h
+example.o:	$(PUBLIC_INTERFACE)
+
+test/run_tests.o: test/UsefulBuf_Tests.h test/float_tests.h test/run_tests.h test/qcbor_encode_tests.h test/qcbor_decode_tests.h inc/qcbor/qcbor_private.h
 test/UsefulBuf_Tests.o: test/UsefulBuf_Tests.h inc/qcbor/UsefulBuf.h
 test/qcbor_encode_tests.o: test/qcbor_encode_tests.h $(PUBLIC_INTERFACE)
 test/qcbor_decode_tests.o: test/qcbor_decode_tests.h $(PUBLIC_INTERFACE)
@@ -62,7 +61,6 @@
 
 cmd_line_main.o: test/run_tests.h $(PUBLIC_INTERFACE)
 
-min_use_main.o: $(PUBLIC_INTERFACE)
 
 ifeq ($(PREFIX),)
     PREFIX := /usr/local
@@ -76,6 +74,7 @@
 	install -m 644 inc/qcbor/qcbor_private.h $(DESTDIR)$(PREFIX)/include/qcbor
 	install -m 644 inc/qcbor/qcbor_common.h $(DESTDIR)$(PREFIX)/include/qcbor
 	install -m 644 inc/qcbor/qcbor_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
+	install -m 644 inc/qcbor/qcbor_spiffy_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
 	install -m 644 inc/qcbor/qcbor_encode.h $(DESTDIR)$(PREFIX)/include/qcbor
 	install -m 644 inc/qcbor/UsefulBuf.h $(DESTDIR)$(PREFIX)/include/qcbor
 
@@ -91,4 +90,4 @@
 		libqcbor.a libqcbor.so libqcbor.so.1 libqcbor.so.1.0.0)
 
 clean:
-	rm -f $(QCBOR_OBJ) $(TEST_OBJ) libqcbor.a min_use_main.o cmd_line_main.o libqcbor.a libqcbor.so qcbormin qcbortest
+	rm -f $(QCBOR_OBJ) $(TEST_OBJ) libqcbor.a cmd_line_main.o libqcbor.a libqcbor.so qcbormin qcbortest
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index 8a665fd..1983591 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -14,17 +14,32 @@
 		E73B575E2161CA7C0080D658 /* float_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* float_tests.c */; };
 		E73B575F2161CA7C0080D658 /* half_to_double_from_rfc7049.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */; };
 		E73B57652161F8F80080D658 /* run_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57632161F8F70080D658 /* run_tests.c */; };
-		E743D0F624BF0C140017899F /* qcbor_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08C214AE07400E67947 /* qcbor_encode.c */; };
-		E743D0F724BF0C140017899F /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
-		E743D0F824BF0C140017899F /* half_to_double_from_rfc7049.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */; };
-		E743D0F924BF0C140017899F /* run_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57632161F8F70080D658 /* run_tests.c */; };
-		E743D0FA24BF0C140017899F /* qcbor_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08E214AE07500E67947 /* qcbor_decode.c */; };
-		E743D0FB24BF0C140017899F /* float_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* float_tests.c */; };
-		E743D0FC24BF0C140017899F /* qcbor_decode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB5216CE6CA00BA646B /* qcbor_decode_tests.c */; };
-		E743D0FD24BF0C140017899F /* UsefulBuf.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08D214AE07500E67947 /* UsefulBuf.c */; };
-		E743D0FE24BF0C140017899F /* qcbor_encode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB8216DC7AD00BA646B /* qcbor_encode_tests.c */; };
-		E743D0FF24BF0C140017899F /* cmd_line_main.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E096214AE0C700E67947 /* cmd_line_main.c */; };
-		E743D10024BF0C140017899F /* UsefulBuf_Tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEBC216DE31700BA646B /* UsefulBuf_Tests.c */; };
+		E743D0F324AD08020017899F /* example.c in Sources */ = {isa = PBXBuildFile; fileRef = E743D0E124AC516D0017899F /* example.c */; };
+		E743D10B24DD4EF50017899F /* qcbor_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08C214AE07400E67947 /* qcbor_encode.c */; };
+		E743D10C24DD4EF50017899F /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
+		E743D10D24DD4EF50017899F /* half_to_double_from_rfc7049.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */; };
+		E743D10E24DD4EF50017899F /* run_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57632161F8F70080D658 /* run_tests.c */; };
+		E743D10F24DD4EF50017899F /* qcbor_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08E214AE07500E67947 /* qcbor_decode.c */; };
+		E743D11024DD4EF50017899F /* float_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* float_tests.c */; };
+		E743D11124DD4EF50017899F /* qcbor_decode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB5216CE6CA00BA646B /* qcbor_decode_tests.c */; };
+		E743D11224DD4EF50017899F /* UsefulBuf.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08D214AE07500E67947 /* UsefulBuf.c */; };
+		E743D11324DD4EF50017899F /* qcbor_encode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB8216DC7AD00BA646B /* qcbor_encode_tests.c */; };
+		E743D11424DD4EF50017899F /* cmd_line_main.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E096214AE0C700E67947 /* cmd_line_main.c */; };
+		E743D11524DD4EF50017899F /* UsefulBuf_Tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEBC216DE31700BA646B /* UsefulBuf_Tests.c */; };
+		E743D11C24DD51340017899F /* example.c in Sources */ = {isa = PBXBuildFile; fileRef = E743D0E124AC516D0017899F /* example.c */; };
+		E743D11D24DD51350017899F /* example.c in Sources */ = {isa = PBXBuildFile; fileRef = E743D0E124AC516D0017899F /* example.c */; };
+		E743D12024DE05CC0017899F /* qcbor_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08C214AE07400E67947 /* qcbor_encode.c */; };
+		E743D12124DE05CC0017899F /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
+		E743D12224DE05CC0017899F /* half_to_double_from_rfc7049.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */; };
+		E743D12324DE05CC0017899F /* run_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57632161F8F70080D658 /* run_tests.c */; };
+		E743D12424DE05CC0017899F /* qcbor_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08E214AE07500E67947 /* qcbor_decode.c */; };
+		E743D12524DE05CC0017899F /* float_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* float_tests.c */; };
+		E743D12624DE05CC0017899F /* qcbor_decode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB5216CE6CA00BA646B /* qcbor_decode_tests.c */; };
+		E743D12724DE05CC0017899F /* UsefulBuf.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08D214AE07500E67947 /* UsefulBuf.c */; };
+		E743D12824DE05CC0017899F /* qcbor_encode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB8216DC7AD00BA646B /* qcbor_encode_tests.c */; };
+		E743D12924DE05CC0017899F /* cmd_line_main.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E096214AE0C700E67947 /* cmd_line_main.c */; };
+		E743D12A24DE05CC0017899F /* example.c in Sources */ = {isa = PBXBuildFile; fileRef = E743D0E124AC516D0017899F /* example.c */; };
+		E743D12B24DE05CC0017899F /* UsefulBuf_Tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEBC216DE31700BA646B /* UsefulBuf_Tests.c */; };
 		E772021723B52C02006E966E /* qcbor_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08C214AE07400E67947 /* qcbor_encode.c */; };
 		E772021823B52C02006E966E /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
 		E772021923B52C02006E966E /* half_to_double_from_rfc7049.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */; };
@@ -40,10 +55,20 @@
 		E776E090214AE07500E67947 /* UsefulBuf.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08D214AE07500E67947 /* UsefulBuf.c */; };
 		E776E091214AE07500E67947 /* qcbor_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E08E214AE07500E67947 /* qcbor_decode.c */; };
 		E776E097214AE0C700E67947 /* cmd_line_main.c in Sources */ = {isa = PBXBuildFile; fileRef = E776E096214AE0C700E67947 /* cmd_line_main.c */; };
+		E7864766252CE63100A0C11B /* qcbor_err_to_str.c in Sources */ = {isa = PBXBuildFile; fileRef = E7864765252CE63100A0C11B /* qcbor_err_to_str.c */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
-		E743D10224BF0C140017899F /* CopyFiles */ = {
+		E743D11724DD4EF50017899F /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+		E743D12D24DE05CC0017899F /* CopyFiles */ = {
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
 			dstPath = /usr/share/man/man1/;
@@ -88,25 +113,38 @@
 		E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = half_to_double_from_rfc7049.c; path = test/half_to_double_from_rfc7049.c; sourceTree = "<group>"; };
 		E73B57632161F8F70080D658 /* run_tests.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = run_tests.c; path = test/run_tests.c; sourceTree = "<group>"; tabWidth = 3; };
 		E73B57642161F8F80080D658 /* run_tests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = run_tests.h; path = test/run_tests.h; sourceTree = "<group>"; };
-		E743D10624BF0C140017899F /* QCBOR_Disable_PREFERRED_FLOAT */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR_Disable_PREFERRED_FLOAT; sourceTree = BUILT_PRODUCTS_DIR; };
+		E743D0E124AC516D0017899F /* example.c */ = {isa = PBXFileReference; indentWidth = 3; lastKnownFileType = sourcecode.c.c; path = example.c; sourceTree = "<group>"; tabWidth = 3; };
+		E743D0F224AC54600017899F /* example.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = example.h; sourceTree = "<group>"; };
+		E743D10824CEDE2D0017899F /* qcbor_spiffy_decode.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_spiffy_decode.h; path = inc/qcbor/qcbor_spiffy_decode.h; sourceTree = "<group>"; tabWidth = 3; };
+		E743D11B24DD4EF50017899F /* QCBOR_Disable_HW_Float */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR_Disable_HW_Float; sourceTree = BUILT_PRODUCTS_DIR; };
+		E743D13124DE05CC0017899F /* QCBOR_Disable_Preferred_Float */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR_Disable_Preferred_Float; sourceTree = BUILT_PRODUCTS_DIR; };
+		E743D132251014E60017899F /* Tagging.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = Tagging.md; path = doc/Tagging.md; sourceTree = "<group>"; };
+		E743D13325115A270017899F /* TRY SPIFFY DECODE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "TRY SPIFFY DECODE.md"; sourceTree = "<group>"; };
 		E74BF411245D6713002CE8E8 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/qcbor/UsefulBuf.h; sourceTree = "<group>"; };
 		E772022723B52C02006E966E /* QCBOR_Disable_Exp_Mantissa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR_Disable_Exp_Mantissa; sourceTree = BUILT_PRODUCTS_DIR; };
 		E776E07C214ADF7F00E67947 /* QCBOR */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR; sourceTree = BUILT_PRODUCTS_DIR; };
 		E776E08C214AE07400E67947 /* qcbor_encode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_encode.c; path = src/qcbor_encode.c; sourceTree = "<group>"; tabWidth = 3; };
 		E776E08D214AE07500E67947 /* UsefulBuf.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = UsefulBuf.c; path = src/UsefulBuf.c; sourceTree = "<group>"; tabWidth = 3; };
 		E776E08E214AE07500E67947 /* qcbor_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_decode.c; path = src/qcbor_decode.c; sourceTree = "<group>"; tabWidth = 3; };
-		E776E093214AE08B00E67947 /* qcbor.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor.h; path = inc/qcbor.h; sourceTree = "<group>"; tabWidth = 3; };
 		E776E094214AE09700E67947 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/UsefulBuf.h; sourceTree = "<group>"; tabWidth = 3; };
 		E776E096214AE0C700E67947 /* cmd_line_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_line_main.c; sourceTree = "<group>"; };
 		E776E161214EE19C00E67947 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
-		E78C91DE240C90C100F4CECE /* qcbor_decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_decode.h; path = inc/qcbor/qcbor_decode.h; sourceTree = "<group>"; };
-		E78C91DF240C90C100F4CECE /* qcbor_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_common.h; path = inc/qcbor/qcbor_common.h; sourceTree = "<group>"; };
-		E78C91E0240C90C100F4CECE /* qcbor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_private.h; path = inc/qcbor/qcbor_private.h; sourceTree = "<group>"; };
-		E78C91E1240C90C100F4CECE /* qcbor_encode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_encode.h; path = inc/qcbor/qcbor_encode.h; sourceTree = "<group>"; };
+		E7864765252CE63100A0C11B /* qcbor_err_to_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_err_to_str.c; path = src/qcbor_err_to_str.c; sourceTree = "<group>"; };
+		E78C91DE240C90C100F4CECE /* qcbor_decode.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_decode.h; path = inc/qcbor/qcbor_decode.h; sourceTree = "<group>"; tabWidth = 3; };
+		E78C91DF240C90C100F4CECE /* qcbor_common.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_common.h; path = inc/qcbor/qcbor_common.h; sourceTree = "<group>"; tabWidth = 3; };
+		E78C91E0240C90C100F4CECE /* qcbor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_private.h; path = inc/qcbor/qcbor_private.h; sourceTree = "<group>"; tabWidth = 3; };
+		E78C91E1240C90C100F4CECE /* qcbor_encode.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_encode.h; path = inc/qcbor/qcbor_encode.h; sourceTree = "<group>"; tabWidth = 3; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
-		E743D10124BF0C140017899F /* Frameworks */ = {
+		E743D11624DD4EF50017899F /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		E743D12C24DE05CC0017899F /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -130,14 +168,26 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		E743D0E024AC51470017899F /* example */ = {
+			isa = PBXGroup;
+			children = (
+				E743D0E124AC516D0017899F /* example.c */,
+				E743D0F224AC54600017899F /* example.h */,
+			);
+			name = example;
+			sourceTree = "<group>";
+		};
 		E776E073214ADF7F00E67947 = {
 			isa = PBXGroup;
 			children = (
 				E776E161214EE19C00E67947 /* README.md */,
+				E743D13325115A270017899F /* TRY SPIFFY DECODE.md */,
+				E743D132251014E60017899F /* Tagging.md */,
 				E776E096214AE0C700E67947 /* cmd_line_main.c */,
 				E776E092214AE07C00E67947 /* inc */,
 				E776E08B214AE06600E67947 /* src */,
 				E776E095214AE0B600E67947 /* test */,
+				E743D0E024AC51470017899F /* example */,
 				E776E07D214ADF7F00E67947 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -147,7 +197,8 @@
 			children = (
 				E776E07C214ADF7F00E67947 /* QCBOR */,
 				E772022723B52C02006E966E /* QCBOR_Disable_Exp_Mantissa */,
-				E743D10624BF0C140017899F /* QCBOR_Disable_PREFERRED_FLOAT */,
+				E743D11B24DD4EF50017899F /* QCBOR_Disable_HW_Float */,
+				E743D13124DE05CC0017899F /* QCBOR_Disable_Preferred_Float */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -160,6 +211,7 @@
 				E776E08D214AE07500E67947 /* UsefulBuf.c */,
 				E73B57582161CA690080D658 /* ieee754.c */,
 				E73B57572161CA680080D658 /* ieee754.h */,
+				E7864765252CE63100A0C11B /* qcbor_err_to_str.c */,
 			);
 			name = src;
 			sourceTree = "<group>";
@@ -167,12 +219,12 @@
 		E776E092214AE07C00E67947 /* inc */ = {
 			isa = PBXGroup;
 			children = (
+				E743D10824CEDE2D0017899F /* qcbor_spiffy_decode.h */,
 				E74BF411245D6713002CE8E8 /* UsefulBuf.h */,
 				E78C91DF240C90C100F4CECE /* qcbor_common.h */,
 				E78C91DE240C90C100F4CECE /* qcbor_decode.h */,
 				E78C91E1240C90C100F4CECE /* qcbor_encode.h */,
 				E78C91E0240C90C100F4CECE /* qcbor_private.h */,
-				E776E093214AE08B00E67947 /* qcbor.h */,
 				E776E094214AE09700E67947 /* UsefulBuf.h */,
 			);
 			name = inc;
@@ -201,21 +253,38 @@
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		E743D0F424BF0C140017899F /* QCBOR_Disable_PREFERRED_FLOAT */ = {
+		E743D10924DD4EF50017899F /* QCBOR_Disable_HW_Float */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = E743D10324BF0C140017899F /* Build configuration list for PBXNativeTarget "QCBOR_Disable_PREFERRED_FLOAT" */;
+			buildConfigurationList = E743D11824DD4EF50017899F /* Build configuration list for PBXNativeTarget "QCBOR_Disable_HW_Float" */;
 			buildPhases = (
-				E743D0F524BF0C140017899F /* Sources */,
-				E743D10124BF0C140017899F /* Frameworks */,
-				E743D10224BF0C140017899F /* CopyFiles */,
+				E743D10A24DD4EF50017899F /* Sources */,
+				E743D11624DD4EF50017899F /* Frameworks */,
+				E743D11724DD4EF50017899F /* CopyFiles */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 			);
-			name = QCBOR_Disable_PREFERRED_FLOAT;
+			name = QCBOR_Disable_HW_Float;
 			productName = QCBOR;
-			productReference = E743D10624BF0C140017899F /* QCBOR_Disable_PREFERRED_FLOAT */;
+			productReference = E743D11B24DD4EF50017899F /* QCBOR_Disable_HW_Float */;
+			productType = "com.apple.product-type.tool";
+		};
+		E743D11E24DE05CC0017899F /* QCBOR_Disable_Preferred_Float */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = E743D12E24DE05CC0017899F /* Build configuration list for PBXNativeTarget "QCBOR_Disable_Preferred_Float" */;
+			buildPhases = (
+				E743D11F24DE05CC0017899F /* Sources */,
+				E743D12C24DE05CC0017899F /* Frameworks */,
+				E743D12D24DE05CC0017899F /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = QCBOR_Disable_Preferred_Float;
+			productName = QCBOR;
+			productReference = E743D13124DE05CC0017899F /* QCBOR_Disable_Preferred_Float */;
 			productType = "com.apple.product-type.tool";
 		};
 		E772021523B52C02006E966E /* QCBOR_Disable_Exp_Mantissa */ = {
@@ -281,27 +350,48 @@
 			targets = (
 				E776E07B214ADF7F00E67947 /* QCBOR */,
 				E772021523B52C02006E966E /* QCBOR_Disable_Exp_Mantissa */,
-				E743D0F424BF0C140017899F /* QCBOR_Disable_PREFERRED_FLOAT */,
+				E743D10924DD4EF50017899F /* QCBOR_Disable_HW_Float */,
+				E743D11E24DE05CC0017899F /* QCBOR_Disable_Preferred_Float */,
 			);
 		};
 /* End PBXProject section */
 
 /* Begin PBXSourcesBuildPhase section */
-		E743D0F524BF0C140017899F /* Sources */ = {
+		E743D10A24DD4EF50017899F /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				E743D0F624BF0C140017899F /* qcbor_encode.c in Sources */,
-				E743D0F724BF0C140017899F /* ieee754.c in Sources */,
-				E743D0F824BF0C140017899F /* half_to_double_from_rfc7049.c in Sources */,
-				E743D0F924BF0C140017899F /* run_tests.c in Sources */,
-				E743D0FA24BF0C140017899F /* qcbor_decode.c in Sources */,
-				E743D0FB24BF0C140017899F /* float_tests.c in Sources */,
-				E743D0FC24BF0C140017899F /* qcbor_decode_tests.c in Sources */,
-				E743D0FD24BF0C140017899F /* UsefulBuf.c in Sources */,
-				E743D0FE24BF0C140017899F /* qcbor_encode_tests.c in Sources */,
-				E743D0FF24BF0C140017899F /* cmd_line_main.c in Sources */,
-				E743D10024BF0C140017899F /* UsefulBuf_Tests.c in Sources */,
+				E743D10B24DD4EF50017899F /* qcbor_encode.c in Sources */,
+				E743D10C24DD4EF50017899F /* ieee754.c in Sources */,
+				E743D10D24DD4EF50017899F /* half_to_double_from_rfc7049.c in Sources */,
+				E743D10E24DD4EF50017899F /* run_tests.c in Sources */,
+				E743D10F24DD4EF50017899F /* qcbor_decode.c in Sources */,
+				E743D11024DD4EF50017899F /* float_tests.c in Sources */,
+				E743D11124DD4EF50017899F /* qcbor_decode_tests.c in Sources */,
+				E743D11224DD4EF50017899F /* UsefulBuf.c in Sources */,
+				E743D11324DD4EF50017899F /* qcbor_encode_tests.c in Sources */,
+				E743D11424DD4EF50017899F /* cmd_line_main.c in Sources */,
+				E743D11D24DD51350017899F /* example.c in Sources */,
+				E743D11524DD4EF50017899F /* UsefulBuf_Tests.c in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		E743D11F24DE05CC0017899F /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				E743D12024DE05CC0017899F /* qcbor_encode.c in Sources */,
+				E743D12124DE05CC0017899F /* ieee754.c in Sources */,
+				E743D12224DE05CC0017899F /* half_to_double_from_rfc7049.c in Sources */,
+				E743D12324DE05CC0017899F /* run_tests.c in Sources */,
+				E743D12424DE05CC0017899F /* qcbor_decode.c in Sources */,
+				E743D12524DE05CC0017899F /* float_tests.c in Sources */,
+				E743D12624DE05CC0017899F /* qcbor_decode_tests.c in Sources */,
+				E743D12724DE05CC0017899F /* UsefulBuf.c in Sources */,
+				E743D12824DE05CC0017899F /* qcbor_encode_tests.c in Sources */,
+				E743D12924DE05CC0017899F /* cmd_line_main.c in Sources */,
+				E743D12A24DE05CC0017899F /* example.c in Sources */,
+				E743D12B24DE05CC0017899F /* UsefulBuf_Tests.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -319,6 +409,7 @@
 				E772021E23B52C02006E966E /* UsefulBuf.c in Sources */,
 				E772021F23B52C02006E966E /* qcbor_encode_tests.c in Sources */,
 				E772022023B52C02006E966E /* cmd_line_main.c in Sources */,
+				E743D11C24DD51340017899F /* example.c in Sources */,
 				E772022123B52C02006E966E /* UsefulBuf_Tests.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -329,6 +420,7 @@
 			files = (
 				E776E08F214AE07500E67947 /* qcbor_encode.c in Sources */,
 				E73B57592161CA690080D658 /* ieee754.c in Sources */,
+				E7864766252CE63100A0C11B /* qcbor_err_to_str.c in Sources */,
 				E73B575F2161CA7C0080D658 /* half_to_double_from_rfc7049.c in Sources */,
 				E73B57652161F8F80080D658 /* run_tests.c in Sources */,
 				E776E091214AE07500E67947 /* qcbor_decode.c in Sources */,
@@ -337,6 +429,7 @@
 				E776E090214AE07500E67947 /* UsefulBuf.c in Sources */,
 				0FA9BEBA216DC7AD00BA646B /* qcbor_encode_tests.c in Sources */,
 				E776E097214AE0C700E67947 /* cmd_line_main.c in Sources */,
+				E743D0F324AD08020017899F /* example.c in Sources */,
 				0FA9BEBD216DE31700BA646B /* UsefulBuf_Tests.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -344,7 +437,34 @@
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
-		E743D10424BF0C140017899F /* Debug */ = {
+		E743D11924DD4EF50017899F /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES;
+				CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = QCBOR_DISABLE_FLOAT_HW_USE;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				GCC_WARN_PEDANTIC = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		E743D11A24DD4EF50017899F /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES;
+				CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				GCC_WARN_PEDANTIC = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		E743D12F24DE05CC0017899F /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES;
@@ -358,7 +478,7 @@
 			};
 			name = Debug;
 		};
-		E743D10524BF0C140017899F /* Release */ = {
+		E743D13024DE05CC0017899F /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES;
@@ -553,11 +673,20 @@
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		E743D10324BF0C140017899F /* Build configuration list for PBXNativeTarget "QCBOR_Disable_PREFERRED_FLOAT" */ = {
+		E743D11824DD4EF50017899F /* Build configuration list for PBXNativeTarget "QCBOR_Disable_HW_Float" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				E743D10424BF0C140017899F /* Debug */,
-				E743D10524BF0C140017899F /* Release */,
+				E743D11924DD4EF50017899F /* Debug */,
+				E743D11A24DD4EF50017899F /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		E743D12E24DE05CC0017899F /* Build configuration list for PBXNativeTarget "QCBOR_Disable_Preferred_Float" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				E743D12F24DE05CC0017899F /* Debug */,
+				E743D13024DE05CC0017899F /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
diff --git a/README.md b/README.md
index 896c590..1a01b42 100644
--- a/README.md
+++ b/README.md
@@ -2,37 +2,54 @@
 
 QCBOR encodes and decodes [RFC 7049](https://tools.ietf.org/html/rfc7049) CBOR.
 
+## New Version With Spiffy Decode
 **A major new version of QCBOR that makes decoding implementations much**
 **easier is available in the SpiffyDecode branch of this repository.**
-**Your CBOR decoding implementation may be four times less code!**
-**It needs a little more test to be high enough quality for the master branch.** 
+**Your CBOR decoding implementation may be many times fewer lines of code!**
+**The old version is available in the BeforeSpiffyDecode branch.**
 
-## Characteristics
+This new version of QCBOR adds a more powerful decoding API
+called Spiffy Decode. 
+
+* Much easier implementation of decoding of CBOR protocols
+* Decoding implementations parallels encoding implementation
+* Overall smaller code size for implementations decoding multiple and
+  / or complex maps
+* Backwards compatible with previous versions of QCBOR
+
+See section below for more details.
+
+## QCBOR Characteristics
 
 **Implemented in C with minimal dependency** – The only dependencies
-  are C99, <stdint.h>, <stddef.h>, <stdbool.h> and <string.h> making it
-  highly portable. No #ifdefs or compiler options need to be set for it
-  to run correctly.
+  are C99, <stdint.h>, <stddef.h>, <stdbool.h> and <string.h> making
+  it highly portable. <math.h> and <fenv.h> are used too, but their
+  use can disabled. No #ifdefs or compiler options need to be set for
+  QCBOR to run correctly.
 
-**Focused on C / native data representation** – Simpler code because
-  there is no support for encoding/decoding to/from JSON, pretty
-  printing, diagnostic notation... Only encoding from native C
-  representations and decoding to native C representations is supported.
+**Focused on C / native data representation** – Careful conversion of
+  CBOR data types in to C data types, carefully handling over and
+  underflow, strict typing and such so the caller doesn't have to
+  worry so much about this and so code using QCBOR passes static
+  analyzers easier.  Simpler code because there is no support for
+  encoding/decoding to/from JSON, pretty printing, diagnostic
+  notation... Only encoding from native C representations and decoding
+  to native C representations is supported.
 
 **Small simple memory model** – Malloc is not needed. The encode
-  context is 136 bytes, decode context is 144 bytes and the
-  description of decoded data item is 48 bytes. Stack use is light and
+  context is 174 bytes, decode context is 312 bytes and the
+  description of decoded data item is 56 bytes. Stack use is light and
   there is no recursion. The caller supplies the memory to hold the
   encoded CBOR and encode/decode contexts so caller has full control
   of memory usage making it good for embedded implementations that
   have to run in small fixed memory.
 
-**Supports all of RFC 7049 except strict mode** – With some size
-  limits, all data types and formats specified are supported.
-  Decoding indefinite length strings requires a string allocator (see
-  documentation). The most notable part of RFC 7049 not supported is
-  detection of duplicate keys in maps, however, the duplicates are
-  passed up to the caller so it can do duplicate detection.
+**Supports all of RFC 7049 except strict mode and map sorting** – With
+  some size limits, all data types and formats specified are
+  supported. The same decoding API supports both definite and
+  indefinite-length map and array decoding. Decoding indefinite length
+  strings is supported but requires a string allocator (see
+  documentation).
 
 **Extensible and general** – Provides a way to handle data types that
   are not directly supported.
@@ -41,23 +58,27 @@
   discipline for very safe coding and handling of binary data.
 
 **Small code size** – When optimized for size using the compiler -Os
-  option, x86 code is about 4KB (~1.1KB encode, ~2.5KB decode,
-  ~0.4KB common). Other decoders may be smaller, but they may
-  also do less for you, so overall size of the implementation may
-  be larger. For example, QCBOR internally tracks error status
-  so you don't have to check a return code on every operation.
+  option, 64-bit x86 code is about 4KB in its smallest configuration
+  and when only basic functionality is used. 
 
 **Clear documented public interface** – The public interface is
   separated from the implementation. It can be put to use without
   reading the source.
 
-**Comprehensive test suite** – Easy to verify on a new platform
-  or OS with the test suite. The test suite dependencies are also
-  minimal, only additionally requiring <math.h> for floating-point
-  tests.
+**Comprehensive test suite** – Easy to verify on a new platform or OS
+  with the test suite. The test suite dependencies are minimal and the
+  same as the library's.
 
 ## Code Status
 
+This version with spiffy decode in fall of 2020 is a big change
+from the previous versions but is thoroughly tested including
+regression for backwards compatibility with the previous version.
+
+Should the previous version be necessary, it is available in 
+the branch BeforeSpiffyDecode. Please file an issue in GitHub
+to report any problems.
+
 QCBOR was originally developed by Qualcomm. It was [open sourced
 through CAF](https://source.codeaurora.org/quic/QCBOR/QCBOR/) with a
 permissive Linux license, September 2018 (thanks Qualcomm!).
@@ -67,20 +88,13 @@
 the CAF source with some simplifications, tidying up and feature
 additions.
 
-From Nov 3, 2018, the interface and code are fairly stable. Large
-changes are not planned or expected, particularly in the
-interface. The test coverage is pretty good.
-
-On Feb 18 2019, there was an interface change to QCBORDecode_SetUpAllocator
-for external string allocator set up. The change was part of a
-memory access alignment fix and code simplification.
 
 ## Building
 
 There is a simple makefile for the UNIX style command line binary that
 compiles everything to run the tests.
 
-These ten files, the contents of the src and inc directories, make
+These eleven files, the contents of the src and inc directories, make
 up the entire implementation.
 
 * inc
@@ -89,6 +103,7 @@
    * qcbor_common.h
    * qcbor_encode.h
    * qcbor_decode.h
+   * qcbor_spiffy_decode.h
 * src
    * UsefulBuf.c
    * qcbor_encode.c
@@ -114,28 +129,94 @@
  * to improve performance (a little)
  * remove features to reduce code size
 
-See the comment sections on "Configuration" in inc/UsefulBuf.h.
+See the comment sections on "Configuration" in inc/UsefulBuf.h and 
+the pre processor defines that start with QCBOR_DISABLE_XXX.
+
+## Spiffy Decode
+
+In Fall 2020 a large addition makes the decoder more powerful and easy
+to use. Backwards compatibility with the previous API is retained as
+the new decoding features layer on top of it.
+
+The first noticable addition are functions to get particular data
+types.  These are an alternative to and built on top of
+QCBORDecode_GetNext() that does the type checking and in some cases
+sophisticated type conversion. They track an error state internally so
+the caller doesn't need to.  They also handle the CBOR tagged data types
+thoroughly and properly.
+
+In line with all the new get functions for non-aggregate types there
+are new functions for aggregate types. When a map is expected,
+QCBORDecode_EnterMap() can be called to descend into and searched by label. 
+Duplicate detection of map items
+is performed. There is a similar facility for arrays and byte-string
+wrapped CBOR.
+
+An outcome of all this is that now the decoding implementation of some
+data can look very similar to the encoding of some data and is
+generally easier to implement. Following is an example of first
+encoding a map with three items and then decoding it.
+
+     /* Encode */
+     QCBOREncode_Init(&EncodeCtx, Buffer);
+     QCBOREncode_OpenMap(&EncodeCtx);
+     QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pE->Manufacturer);
+     QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pE->uDisplacement);
+     QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pE->uHorsePower);
+     QCBOREncode_CloseMap(&EncodeCtx);
+     uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedEngine);
+  
+     /* Decode */
+     QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
+     QCBORDecode_EnterMap(&DecodeCtx);
+     QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
+     QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
+     QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
+     QCBORDecode_ExitMap(&DecodeCtx);
+     uErr = QCBORDecode_Finish(&DecodeCtx);
+
+The spiffy decode version of QCBOR also handles CBOR tags in a simpler 
+and more thorough way.
+
+The spiffy decode functions will handle definite and indefinite length
+maps and arrays without the caller having to do anything. This includes 
+mixed definite and indefinte maps and arrays. (Some work remains to
+support map searching with indefinite length strings.)
+
+See the PR in GitHub for a more detailed list of changes.
+
+### Uncompatible Changes
+
+Encoding of MIME tags now uses tag 257 instead of 36. Tag 257 accommodates
+binary and text-based MIME messages where tag 36 does not. Decoding
+supports either.
+
+The number of nested tags on a data item is limited to four. Previously it was
+unlimited.
+
+Some of the error codes have changed.
 
 ## Floating Point Support
 
-By default, all floating-point features are supported. This includes
+By default, all QCBOR floating-point features are enabled. This includes
 encoding and decoding of half-precision, CBOR preferred serialization
 for floating-point and floating-point epoch dates.
 
 If full floating-point is not needed the following #defines can be
 used to reduce object code size.
 
-QCBOR_DISABLE_FLOAT_HW_USE -- Avoid all use of floating-point hardware.
+QCBOR_DISABLE_FLOAT_HW_USE -- Avoid all use of floating-point hardware
+and inclusion of <math.h> and <fenv.h>
 
 QCBOR_DISABLE_PREFERRED_FLOAT -- Eliminates support for half-precision
 and CBOR preferred serialization.
 
 Even if these are #defined, QCBOR can still encode and decode basic
-floating point.
+floating point numbers.
 
 Defining QCBOR_DISABLE_PREFERRED_FLOAT will reduce object code size by
 about 900 bytes, though 550 of these bytes can be avoided without the
-define by just not calling any of the functions that encode
+#define by just not calling any of the functions that encode
 floating-point numbers.
 
 Defining QCBOR_DISABLE_FLOAT_HW_USE will save a small amount of object
@@ -145,36 +226,84 @@
 
 ## Code Size
 
-These are approximate sizes on 64-bit x86 with the -Os optimization.
+These are approximate sizes on a 64-bit x86 CPU with the -Os optimization.
 
     |               | smallest | largest |  
     |---------------|----------|---------|
-    | encode only   |     1000 |    2100 |
-    | decode only   |     2500 |    4300 |
-    | combined      |     3500 |    6400 |
+    | encode only   |      900 |    2100 |
+    | decode only   |     2800 |   13500 |
+    | combined      |     3700 |   15600 |
     
-The following are some ways to make the code smaller.
+ From the table above, one can see that the amount of code pulled in
+ from the QCBOR library varies a lot, ranging from 1KB to 15KB.  The
+ main factor is in this is the number of QCBOR functions called and
+ which ones they are. QCBOR is constructed with less internal
+ interdependency so only code necessary for the called functions is
+ brought in.
+ 
+ Encoding is simpler and smaller. An encode-only implementation may
+ bring in only 1KB of code.
+ 
+ Encoding of floating-point brings in a little more code as does
+ encoding of tagged types and encoding of bstr wrapping.
+ 
+ Basic decoding using QCBORDecode_GetNext() brings in 3KB.
+ 
+ Use of the supplied MemPool by calling _QCBORDecode_SetMemPool() to
+ setup to decode indefinite length strings adds 0.5KB.
+ 
+ Basic use of spiffy decode to brings in about 3KB. Using more spiffy
+ decode functions, such as those for tagged types bstr wrapping brings
+ in more code.
+ 
+ Finally, use of all of the integer conversion functions will bring in
+ about 5KB, though you can use the simpler ones like
+ QCBORDecode_GetInt64() without bringing in very much code.
+ 
+ In addition to using fewer QCBOR functions, the following are some
+ ways to make the code smaller.
 
-The gcc compiler output is usually smaller than llvm because stack
-guards are off by default (be sure you actually have gcc and not llvm
-installed to be invoked by the gcc command). You can also turn off
-stack gaurds with llvm. It is safe to turn off stack guards with this
-code because Usefulbuf provides similar defenses and this code was
-carefully written to be defensive.
+ The gcc compiler output is usually smaller than llvm because stack
+ guards are off by default (be sure you actually have gcc and not llvm
+ installed to be invoked by the gcc command). You can also turn off
+ stack gaurds with llvm. It is safe to turn off stack gaurds with this
+ code because Usefulbuf provides similar defenses and this code was
+ carefully written to be defensive.
 
-Use fewer of the encode functions, particularly avoid floating-point
-and bstr wrapping. This combined with dead-stripping works very well
-to automatically omit functions that are not needed on the encode
-side.
+ Disable features with defines like
+ QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA (saves about 400 bytes) and
+ QCBOR_DISABLE_PREFERRED_FLOAT (saves about 900 bytes).  More of these
+ defines are planned than are currently implemented (they are a little
+ complex to implement because all the combination configurations must
+ be tested).
+ 
+ If QCBOR is installed as a shared library, then of course only one
+ copy of the code is in memory no matter how many applications use it.
+ 
+ ### Size of spiffy decode
+ 
+ When creating a decode implementation, there is a choice of whether
+ or not to use spiffy decode features or to just use
+ QCBORDecode_GetNext().
+ 
+ The implementation using spiffy decode will be simpler resulting in
+ the calling code being smaller, but the amount of code brought in
+ from the QCBOR library will be larger. Basic use of spiffy decode
+ brings in about 2KB of object code.  If object code size is not a
+ concern, then it is probably better to use spiffy decode because it
+ is less work, there is less complexity and less testing to worry
+ about.
+ 
+ If code size is a concern, then use of QCBORDecode_GetNext() will
+ probably result in smaller overall code size for simpler CBOR
+ protocols. However, if the CBOR protocol is complex then use of
+ spiffy decode may reduce overall code size.  An example of a complex
+ protocol is one that involves decoding a lot of maps or maps that
+ have many data items in them.  The overall code may be smaller
+ because the general purpose spiffy decode map processor is the one
+ used for all the maps.
 
-Disable features with defines like
-QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA (saves about 400 bytes) and
-QCBOR_DISABLE_PREFERRED_FLOAT (saves about 900 bytes).  This is the
-primary means of reducing code on the decode side.  More of these
-defines are planned than are currently implemented, but they are a
-little complex to implement because all the combination configurations 
-must be tested.
-
+ 
 ## Other Software Using QCBOR
 
 * [t_cose](https://github.com/laurencelundblade/t_cose) implements enough of
@@ -258,7 +387,4 @@
 
 ### Copyright for this README
 
-Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
-
-
-
+Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
diff --git a/TRY SPIFFY DECODE.md b/TRY SPIFFY DECODE.md
deleted file mode 100644
index 2f0c4a4..0000000
--- a/TRY SPIFFY DECODE.md
+++ /dev/null
@@ -1,40 +0,0 @@
-#  Try Spiffy Decode
-
-A major new version of QCBOR has Spiffy Decode which makes it much easier to 
-implement CBOR decoding.
-
-The decoding implementation of a protocol looks like the encoding implementation:
-
-     /* Encode */
-     QCBOREncode_Init(&EncodeCtx, Buffer);
-     QCBOREncode_OpenMap(&EncodeCtx);
-     QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pE->Manufacturer);
-     QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pE->uDisplacement);
-     QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pE->uHorsePower);
-     QCBOREncode_CloseMap(&EncodeCtx);
-     uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedEngine);
-  
-     /* Decode */
-     QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
-     QCBORDecode_EnterMap(&DecodeCtx);
-     QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
-     QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
-     QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
-     QCBORDecode_ExitMap(&DecodeCtx);
-     uErr = QCBORDecode_Finish(&DecodeCtx);
-
-With the previous decoding API (which is still supported), the decode implementation 
-is 5-10 times longer.
-
-While the QCBOR library is larger, it reduces the object code needed to call it by a lot,
-so if you have to decode multiple maps, overall object code is likely to go down.
-
-While this new version is backwards compatible, it is a large change so it is not
-yet merged into the QCBOR master. It needs more testing before that happens.
-
-This new version is in SpiffyDecode branch so you have to explicitly go and get that branch.
-
-This version also improves tag handling a lot, adds duplicate map label detection and fixes
-some bugs with CBOR that is a combination of definite and indefinite lengths.
-
-
diff --git a/cmd_line_main.c b/cmd_line_main.c
index f7c6010..ef59e43 100644
--- a/cmd_line_main.c
+++ b/cmd_line_main.c
@@ -12,6 +12,7 @@
 
 #include <stdio.h>
 #include "run_tests.h"
+#include "example.h"
 
 
 /*
@@ -31,6 +32,9 @@
 {
    (void)argc; // Avoid unused parameter error
 
+   RunQCborExample();
+
+
    // This call prints out sizes of data structures to remind us
    // to keep them small.
    PrintSizesQCBOR(&fputs_wrapper, stdout);
diff --git a/doc/DocReadMe.txt b/doc/DocReadMe.txt
new file mode 100644
index 0000000..82f73f0
--- /dev/null
+++ b/doc/DocReadMe.txt
@@ -0,0 +1,4 @@
+Most documentation is in the header files in ../inc/qcbor/. 
+The files here are additional articles.
+There is cross-referencing between these articles and the header files.
+
diff --git a/doc/Tagging.md b/doc/Tagging.md
new file mode 100644
index 0000000..7d65185
--- /dev/null
+++ b/doc/Tagging.md
@@ -0,0 +1,237 @@
+@anchor CBORTags
+
+#  Types and Tagging in CBOR
+
+## New Types
+
+CBOR provides a means for defining new data types beyond the primitive
+types like integers and strings. These new types can be the simple
+association of some further semantics with a primitive type or they
+can be large complex aggregations.
+
+The explicit means for identifying these new types as called tagging.
+It uses a simple unsigned integer known as the tag number to indicate
+that the following CBOR is of that type. Officially speaking, a "tag"
+is made up of exactly a tag number and tag content.  The tag content
+is exactly a single data item, but note that a single data item can be
+a map or an array which contains further nested maps and arrays and
+thus arbitrarily complex.
+
+The tag numbers can be up to UINT64_MAX, so there are a lot of them.
+Some defined in standards and registered in the IANA CBOR Tag
+Registry. A very large range is available for proprietary tags.
+
+## Are Tags "Optional"?
+
+The description of tags in
+[RFC 7049] (https://tools.ietf.org/html/rfc7049) and in some other
+places can lead one to think they are optional prefixes that can be
+ignored at times. This is not true.
+
+As stated above, a tag is exactly a tag number and a single data item
+that is the tag content. Its purpose in the encoded CBOR is to
+indicate something is of a data type. Ignoring it would be like
+ignoring a typedef or struct in C.
+
+However, it is common in CBOR-based protocols to use the format,
+semantics or definition of the tag content without it actually being a
+*tag*. One can think of this as *borrowing* the tag content or implied
+type information.
+
+For example, [RFC 8392] (https://tools.ietf.org/html/rfc8392) which
+defines a CBOR Web Token, a CWT, says that the NumericDate field is
+represented as a CBOR numeric date described as tag 1 in the CBOR
+standard, but with the tag number 1 omitted from the encoding. A
+NumericDate is thus not a tag. It just borrows the content format and
+semantics from tag 1.
+
+This borrowing of the content makes a lot of sense for data items that
+are labeled members of a map where the type of the data can be easily
+inferred by the label and the full use of a tag with a tag number
+would be redundant.
+
+There is another way that tags are "optional". RFC 8392 serves again
+as an example. A CWT is officially defined as a COSE-secured map
+containing a bunch of claims where each claim is a labeled data
+item. This COSE-secured map-of-claims is the definition of a *CWT* and
+stands on its own as the definition of a protocol message. One can say
+that some protocol message is a *CWT* without ever mention the word
+tag or the *CWT Tag*.
+
+Then RFC 8392 goes on to define a *CWT Tag* as a tag with tag number
+of 61 and tag content being a *CWT*. The content format definition
+comes first and stands on it's own.
+
+To recap, the tags defined in RFC 7049 such as the date formats define
+the content type of the tag only in the context of the tag itself. To
+use the content formats outside of the tag, the content format must be
+borrowed.  By contrast some definitions first define the content
+format in an independent way, then they define a tag to enclose that
+particular content format. A CWT is of the later sort.
+
+Finally, every CBOR protocol should explicitly spell out how it is
+using each tag, borrowing tag content and such. If the protocol you
+are trying to implement doesn't, ask the designer.  Generally,
+protocols designs should not allow for some data item to optional be
+either a tag or to be the borrowed tag content.  While allowing this
+tag optionality is a form of Postel's law, "be liberal in what you
+accept", current wisdom is somewhat the opposite.
+
+
+## Types and Tags in QCBOR
+
+QCBOR explicitly supports all the tags defined in
+[RFC 7049] (https://tools.ietf.org/html/rfc7049). It has specific APIs
+and data structures for encoding and decoding them.
+
+These APIs and structures can support either the full and proper tag
+form or the borrowed content form that is not a tag.
+
+The original QCBOR APIs for encoding tags did not allow for encoding
+the borrowed content format. They only support the proper tag
+format. With spiffy decode, a second set of APIs was added that takes
+and argument to indicate whether the proper tag should be output or
+just the borrowed content format should be output. The first set are
+the "AddXxxx" functions and the second the "AddTXxxx" functions.
+
+When decoding with QCBORDecode_GetNext(), the non-spiffy decode API,
+the proper tag form is automatically recognized by the tag number and
+decoded into QCBORItem. This decoding API however cannot recognize
+borrowed content format. The caller must tell QCBOR when borrowed
+content format is expected.
+
+The spiffy decode APIs for the particular tags are the way the caller
+tells QCBOR to expect borrowed content format. These spiffy decode
+APIs can also decode the proper tag as well. When asked to decode a
+proper tag and the input CBOR is not, it is a decode validity
+error. These APIs take an argument which says whether to expect the
+proper tag or just the borrowed content. They can also be told to
+allow either to "be liberal in what you accept", but this is not
+recommended.
+
+
+## Nested Tags
+
+CBOR tags are an enclosing or encapsulating format. When one tag
+encloses another, the enclosed tag is the content for the enclosing
+tag.
+
+Encoding nested tags is easy with QCBOREncode_AddTag(). Just call it
+several times before calling the functions to encode the tag content.
+
+When QCBOR decodes tags it does so by first completely processing the
+built-in tags that it knows how to process. It returns that processed
+item.
+
+If tags occur that QCBOR doesn't know how to process, it will return
+the tag content as a @ref QCBORItem and list the tags that
+encapsulate. The caller then has the information it needs to process
+tag that QCBOR did not.
+
+Nesting of tags is certainly used in CBOR protocols, but deep nesting
+is less common so QCBOR has an implementation limit of 4 levels of tag
+encapsulation on some tag content. (This can be increased by changing
+QCBOR_MAX_TAGS_PER_ITEM, but it will increase stack memory use by
+increasing the size of a QCBORItem).
+
+QCBOR also saves memory by mapping the tag values larger than
+UINT16_MAX, so the tags have to fetched through an accessor function.
+
+When decoding with QCBORDecode_GetNext(), the encapsulating tags are
+listed in the QCBORItem returned. When decoding with spiffy decoding
+functions the tags encapsulating the last-decoded item are saved in
+the decode context and have to be fetched with
+QCBORDecode_GetNthTagOfLast().
+
+## Tags for Encapsulated CBOR
+
+Tag 24 and 63 deserve special mention. The content of these tags is a
+byte string containing encoded CBOR. The content of tag 24 is a single
+CBOR data item and the content of tag 63 is a CBOR sequence, more than
+one data item. Said another way, with tag 24 you can have deeply
+nested complex structures, but the if you do the one data item must be
+either a map or an array or a tag that defined to be complex and
+nested. With tag 63, the content can be a sequence of integers not
+held together in a map or array. Tag 63 is defined in
+[RFC 8742] (https://tools.ietf.org/html/rfc8742).
+
+The point of encapsulating CBOR this way is often so it can be
+cryptographically signed. It works well with off-the-shelf encoders
+and decoders to sign and verify CBOR this way because the decoder can
+just get the byte string that it needs to hash in a normal way, then
+feed the content back into another instance of the CBOR decoder.
+
+It is also a way to break up complex CBOR structures so they can be
+decoded in layers. Usually, with CBOR one error will render the whole
+structure un-decodable because there is little redundancy in the
+encoding. By nesting like this, an error in the wrapped CBOR will not
+cause decoding error in the wrapping CBOR.
+
+QCBOR can be asked to treat these two tags as nesting like maps and
+arrays are nesting with the spiffy decode
+QCBORDecode_EnterBstrWrapped() decoding function.  It is kind of like
+entering an array with one item, but with the difference that the end
+is defined by the end of the byte string not the end of the array.
+
+These tags work like others in that they can be the proper tag or they
+can be the borrowed content. The QCBOR API supports this as any other
+tag.
+
+Finally, the payload and protected headers of COSE are worth
+mentioning here. Neither are officially tag 24 or 63 though they look
+like it and QCBORs decode APIs can be used on them.
+
+The protected headers are a CBOR byte string that always contains
+encoded CBOR. It could have been described as tag 24 borrowed content.
+
+The payload is always a byte string, but only sometimes contains
+encoded CBOR. It never could have been defined as tag 24. When the
+payload is known to contain CBOR, like the case of a CWT, then QCBOR's
+QCBORDecode_EnterBstrWrapped() can be used to decode it.
+
+## Tags that Can be Ignored
+
+There are a few specially defined tags that can actually be
+ignored. These are the following:
+
+    21 Hint that content should be base64url encoded
+    22 Hint that content should be base64 encoded
+    23 Hint that content should be base16 encoded
+    57799 Tag that serves as a CBOR magic number
+
+The content format for all these tags is that it can be any valid
+CBOR. Decoding of these tags doesn't have to check the content format.
+
+Tag 55799 is not really for consumption by the CBOR decoder. Rather it
+is for file format checkers and such.  The other tags are just hints
+in how to process the content. They don't really create new data types
+with new semantics.
+
+Other than these four, just about every other tag defined thus far
+requires the content to be of a specific type and results in a new
+data type that a protocol decoder must understand.
+
+
+## Standard Tags and the Tags Registry
+
+Tags used in CBOR protocols should at least be registered in the
+[IANA CBOR Tags Registry] (https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml).
+A small number of tags (0-23), are full IETF standards. Further, tags
+24-255 require published documentation, but are not full IETF
+standards. Beyond tag 255, the tags are first come first served.
+
+There is no range for private use, so any tag used in a CBOR protocol
+should be registered. The range of tag values is very large to
+accommodate this.
+
+As described above, It is common to use data types from the registry
+in a CBOR protocol without the explicit tag, so in a way the registry
+is a registry of data types.
+
+
+## See Also
+
+See @ref Tags-Overview and @ref Tag-Usage.
+
+
+
diff --git a/TimeTag1FAQ.md b/doc/TimeTag1FAQ.md
similarity index 100%
rename from TimeTag1FAQ.md
rename to doc/TimeTag1FAQ.md
diff --git a/doxygen/Doxyfile b/doxygen/Doxyfile
new file mode 100644
index 0000000..e93d624
--- /dev/null
+++ b/doxygen/Doxyfile
@@ -0,0 +1,2583 @@
+# Doxyfile 1.8.18
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = QCBOR
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 5
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# (including Cygwin) ands Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = YES
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  =  \
+                         /Users/lgl/Code/QCBOR/AdvancedDecode/inc/qcbor/...
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
+# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.doc \
+                         *.txt \
+                         *.py \
+                         *.pyw \
+                         *.f90 \
+                         *.f95 \
+                         *.f03 \
+                         *.f08 \
+                         *.f18 \
+                         *.f \
+                         *.for \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf \
+                         *.ice
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
+# were built. This is equivalent to specifying the "-p" option to a clang tool,
+# such as clang-check. These options will then be passed to the parser.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png The default and svg Looks nicer but requires the
+# pdf2svg tool.
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT    = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE      =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = https://cdn.jsdelivr.net/npm/mathjax@2
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         =
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/example.c b/example.c
new file mode 100644
index 0000000..042c3b3
--- /dev/null
+++ b/example.c
@@ -0,0 +1,715 @@
+/* =========================================================================
+   example.c -- Example code for QCBOR
+
+   Copyright (c) 2020, Laurence Lundblade. All rights reserved.
+
+   SPDX-License-Identifier: BSD-3-Clause
+
+   See BSD-3-Clause license in README.md
+
+   Created on 6/30/2020
+  ========================================================================== */
+
+#include <stdio.h>
+#include "example.h"
+#include "qcbor/qcbor_encode.h"
+#include "qcbor/qcbor_decode.h"
+#include "qcbor/qcbor_spiffy_decode.h"
+
+
+#define MAX_CYLINDERS 16
+
+/**
+ The data structure representing a car engine that is encoded and
+ decoded in this example.
+ */
+typedef struct
+{
+   UsefulBufC Manufacturer;
+   int64_t    uDisplacement;
+   int64_t    uHorsePower;
+   double     dDesignedCompresion;
+   int64_t    uNumCylinders;
+   bool       bTurboCharged;
+   struct {
+      double uMeasuredCompression;
+   } cylinders[MAX_CYLINDERS];
+} CarEngine;
+
+
+/**
+ @brief Initialize the Engine data structure with values to encode/decode.
+
+ @param[out] pE   The Engine structure to fill in
+ */
+void EngineInit(CarEngine *pE)
+{
+   pE->Manufacturer        = UsefulBuf_FROM_SZ_LITERAL("Porsche");
+   pE->uDisplacement       = 3296;
+   pE->uHorsePower         = 210;
+   pE->dDesignedCompresion = 9.1;
+   pE->uNumCylinders       = 6;
+   pE->bTurboCharged       = false;
+
+   pE->cylinders[0].uMeasuredCompression = 9.0;
+   pE->cylinders[1].uMeasuredCompression = 9.2;
+   pE->cylinders[2].uMeasuredCompression = 8.9;
+   pE->cylinders[3].uMeasuredCompression = 8.9;
+   pE->cylinders[4].uMeasuredCompression = 9.1;
+   pE->cylinders[5].uMeasuredCompression = 9.0;
+}
+
+
+/**
+ @brief Compare two Engine structure for equality.
+
+ @param[in] pE1  First Engine to compare.
+ @param[in] pE2  Second Engine to compare.
+
+ @retval Return @c true if the two Engine data structures are exactly the
+ same.
+ */
+bool EngineCompare(CarEngine *pE1, CarEngine *pE2)
+{
+    if(pE1->uNumCylinders != pE2->uNumCylinders) {
+        return false;
+    }
+    if(pE1->bTurboCharged != pE2->bTurboCharged) {
+        return false;
+    }
+    if(pE1->uDisplacement != pE2->uDisplacement) {
+        return false;
+    }
+    if(pE1->uHorsePower != pE2->uHorsePower) {
+        return false;
+    }
+    if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
+        return false;
+    }
+    for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
+        if(pE1->cylinders[i].uMeasuredCompression !=
+           pE2->cylinders[i].uMeasuredCompression) {
+            return false;
+        }
+    }
+
+    if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
+        return false;
+    }
+
+    return true;
+}
+
+
+#ifndef EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE
+/**
+ @brief Encode an initialized Engine data structure in CBOR.
+
+ @param[in] pEngine  The data structure to encode.
+ @param[in] Buffer   Pointer and length of buffer to output to.
+
+ @return  The pointer and length of the encoded CBOR or
+          @ref NULLUsefulBufC on error.
+
+ This is a simple CBOR encoding example. It outputs the Engine data
+ structure as a map of label-value pairs as well as an array of
+ floating point values.
+
+ @c Buffer must be big enough to hold the output. If it is not @ref
+ NULLUsefulBufC will be returned. @ref @ref NULLUsefulBufC will be
+ returned for any other encoding errors.
+
+ This encoding will use definite CBOR lengths. Definite lengths are
+ preferred in CBOR. See EncodeEngineIndefinteLen() that encodes using
+ indefinite lengths.
+ */
+UsefulBufC EncodeEngineDefiniteLength(const CarEngine *pEngine, UsefulBuf Buffer)
+{
+    /* Initialize the encoder with the buffer big enough to hold the
+       expected output.  If it is too small, QCBOREncode_Finish() will
+       return an error. */
+    QCBOREncodeContext EncodeCtx;
+    QCBOREncode_Init(&EncodeCtx, Buffer);
+
+    /* Proceed to output all the items, letting the internal error
+     tracking do its work. */
+    QCBOREncode_OpenMap(&EncodeCtx);
+    QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
+    QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
+    QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
+    QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
+    QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
+    QCBOREncode_OpenArrayInMap(&EncodeCtx, "Cylinders");
+    for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
+        QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
+    }
+    QCBOREncode_CloseArray(&EncodeCtx);
+    QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
+    QCBOREncode_CloseMap(&EncodeCtx);
+
+    /* Get the pointer and length of the encoded output. If there was
+       any error it will be returned here. */
+    UsefulBufC EncodedCBOR;
+    QCBORError uErr;
+    uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
+    if(uErr != QCBOR_SUCCESS) {
+       return NULLUsefulBufC;
+    } else {
+       return EncodedCBOR;
+    }
+}
+#endif /* EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE */
+
+
+
+
+#ifndef EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE_ENCODE
+/**
+ @brief Encode an initialized Engine data structure using indefinite lengths.
+
+ @param[in] pEngine  The data structure to encode.
+ @param[in] Buffer   Pointer and length of buffer to output to.
+
+ @return The pointer and length of the encoded CBOR or
+         @ref NULLUsefulBufC on error.
+
+ This is virtually the same as EncodeEngineDefiniteLength(). The
+ encoded CBOR is slightly different as the map and array use
+ indefinite lengths, rather than definite lengths.
+
+ A definite length array is encoded as an integer indicating the
+ number of items in it. An indefinite length array is encoded as an
+ opening byte, the items in it and a "break" byte to end
+ it. Indefinite length arrays and maps are easier to encode, but
+ harder to decode.
+
+ The advantage of this implementation is that the encoding side will
+ be a little less object code. (Eventually QCBOR will an ifdef to
+ disable definite length encoding and the object code will be even
+ smaller).  However, note that the encoding implementation for a
+ protocol is just about always much smaller than the decoding
+ implementation and that code savings for use of indefinite lengths is
+ relatively small.
+ */
+UsefulBufC EncodeEngineIndefinteLen(const CarEngine *pEngine, UsefulBuf Buffer)
+{
+    QCBOREncodeContext EncodeCtx;
+
+    QCBOREncode_Init(&EncodeCtx, Buffer);
+    QCBOREncode_OpenMapIndefiniteLength(&EncodeCtx);
+    QCBOREncode_AddTextToMap(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
+    QCBOREncode_AddInt64ToMap(&EncodeCtx, "Displacement", pEngine->uDisplacement);
+    QCBOREncode_AddInt64ToMap(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
+    QCBOREncode_AddDoubleToMap(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
+    QCBOREncode_AddInt64ToMap(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
+    QCBOREncode_OpenArrayIndefiniteLengthInMap(&EncodeCtx, "Cylinders");
+    for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
+        QCBOREncode_AddDouble(&EncodeCtx, pEngine->cylinders[i].uMeasuredCompression);
+    }
+    QCBOREncode_CloseArrayIndefiniteLength(&EncodeCtx);
+    QCBOREncode_AddBoolToMap(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
+    QCBOREncode_CloseMapIndefiniteLength(&EncodeCtx);
+
+    UsefulBufC EncodedCBOR;
+    QCBORError uErr;
+    uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
+    if(uErr != QCBOR_SUCCESS) {
+        return NULLUsefulBufC;
+    } else {
+       return EncodedCBOR;
+    }
+}
+#endif /* EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE */
+
+
+/**
+ Error results when decoding an Engine data structure.
+ */
+typedef enum  {
+    EngineSuccess,
+    CBORNotWellFormed,
+    TooManyCylinders,
+    EngineProtocolerror,
+    WrongNumberOfCylinders
+} EngineDecodeErrors;
+
+
+/**
+ Convert @ref QCBORError to @ref EngineDecodeErrors.
+ */
+EngineDecodeErrors ConvertError(QCBORError uErr)
+{
+    EngineDecodeErrors uReturn;
+
+    switch(uErr)
+    {
+        case QCBOR_SUCCESS:
+            uReturn = EngineSuccess;
+            break;
+
+        case QCBOR_ERR_HIT_END:
+            uReturn = CBORNotWellFormed;
+            break;
+
+        default:
+            uReturn = EngineProtocolerror;
+            break;
+    }
+
+    return uReturn;
+}
+
+
+#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE
+/**
+ @brief Simplest engine decode using spiffy decode features.
+
+ @param[in] EncodedEngine  Pointer and length of CBOR-encoded engine.
+ @param[out] pE            The structure filled in from the decoding.
+
+ @return The decode error or success.
+
+ This decodes the CBOR into the engine structure.
+
+ As QCBOR automatically supports both definite and indefinite maps and
+ arrays, this will decode either.
+
+ This uses QCBOR's spiffy decode, so the implementation is simplest
+ and closely parallels the encode implementation in
+ EncodeEngineDefiniteLength().
+
+ See two other ways to implement decoding in
+ DecodeEngineSpiffyFaster() and DecodeEngineBasic().
+
+ This version of the decoder has the simplest implementation, but
+ pulls in more code from the QCBOR library.  This version uses the
+ most CPU cycles because it scans the all the CBOR each time a data
+ item is decoded. The CPU cycles used for a data structure as small as
+ this is probably insignificant. CPU use for this style of decode is
+ probably only a factor on slow CPUs with big CBOR inputs.
+ */
+EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE)
+{
+    QCBORError uErr;
+    QCBORDecodeContext DecodeCtx;
+
+    QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
+    QCBORDecode_EnterMap(&DecodeCtx, NULL);
+    QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
+    QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
+    QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
+    QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
+    QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
+
+    QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
+
+    /* Must check error before referencing pE->uNumCylinders to be
+       sure it is valid. If any of the above errored, it won't be
+       valid. */
+    uErr = QCBORDecode_GetError(&DecodeCtx);
+    if(uErr != QCBOR_SUCCESS) {
+        goto Done;
+    }
+
+    if(pE->uNumCylinders > MAX_CYLINDERS) {
+        return TooManyCylinders;
+    }
+
+    QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
+    for(int64_t i = 0; i < pE->uNumCylinders; i++) {
+        QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
+    }
+    QCBORDecode_ExitArray(&DecodeCtx);
+    QCBORDecode_ExitMap(&DecodeCtx);
+
+    /* Catch the remainder of errors here */
+    uErr = QCBORDecode_Finish(&DecodeCtx);
+
+Done:
+    return ConvertError(uErr);
+}
+
+#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE */
+
+
+
+#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE_FAST
+/**
+ @brief Decode an Engine structure with the faster spiffy decode features.
+
+ @param[in] EncodedEngine  Pointer and length of CBOR-encoded engine.
+ @param[out] pE            The structure filled in from the decoding.
+
+ @return The decode error or success.
+
+ This decodes the same as DecodeEngineSpiffy(), but uses different
+ spiffy decode features.
+
+ This version uses QCBORDecode_GetItemsInMap() which uses less CPU
+ cycles because all the items except the array are pulled out of the
+ map in one pass, rather than having to decode the whole map for each
+ decoded item. This also pulls in less object code from the QCBOR
+ library.
+
+ See also DecodeEngineAdvanced() and DecodeEngineBasic().
+*/
+EngineDecodeErrors DecodeEngineSpiffyFaster(UsefulBufC EncodedEngine, CarEngine *pE)
+{
+    QCBORError uErr;
+    QCBORDecodeContext DecodeCtx;
+
+    QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
+    QCBORDecode_EnterMap(&DecodeCtx, NULL);
+
+    QCBORItem EngineItems[7];
+    EngineItems[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
+    EngineItems[0].label.string = UsefulBuf_FROM_SZ_LITERAL("Manufacturer");
+    EngineItems[0].uDataType = QCBOR_TYPE_TEXT_STRING;
+
+    EngineItems[1].uLabelType = QCBOR_TYPE_TEXT_STRING;
+    EngineItems[1].label.string = UsefulBuf_FROM_SZ_LITERAL("Displacement");
+    EngineItems[1].uDataType = QCBOR_TYPE_INT64;
+
+    EngineItems[2].uLabelType = QCBOR_TYPE_TEXT_STRING;
+    EngineItems[2].label.string = UsefulBuf_FROM_SZ_LITERAL("Horsepower");
+    EngineItems[2].uDataType = QCBOR_TYPE_INT64;
+
+    EngineItems[3].uLabelType = QCBOR_TYPE_TEXT_STRING;
+    EngineItems[3].label.string = UsefulBuf_FROM_SZ_LITERAL("DesignedCompression");
+    EngineItems[3].uDataType = QCBOR_TYPE_DOUBLE;
+
+    EngineItems[4].uLabelType = QCBOR_TYPE_TEXT_STRING;
+    EngineItems[4].label.string = UsefulBuf_FROM_SZ_LITERAL("Turbo");
+    EngineItems[4].uDataType = QCBOR_TYPE_ANY;
+
+    EngineItems[5].uLabelType = QCBOR_TYPE_TEXT_STRING;
+    EngineItems[5].label.string = UsefulBuf_FROM_SZ_LITERAL("NumCylinders");
+    EngineItems[5].uDataType = QCBOR_TYPE_INT64;
+
+    EngineItems[6].uLabelType = QCBOR_TYPE_NONE;
+
+    QCBORDecode_GetItemsInMap(&DecodeCtx, EngineItems);
+    uErr = QCBORDecode_GetError(&DecodeCtx);
+    if(uErr != QCBOR_SUCCESS) {
+        goto Done;
+    }
+
+    pE->Manufacturer = EngineItems[0].val.string;
+    pE->uDisplacement = EngineItems[1].val.int64;
+    pE->uHorsePower = EngineItems[2].val.int64;
+    pE->dDesignedCompresion = EngineItems[3].val.dfnum;
+    pE->uNumCylinders = EngineItems[5].val.int64;
+
+    if(EngineItems[4].uDataType == QCBOR_TYPE_TRUE) {
+        pE->bTurboCharged = true;
+    } else if(EngineItems[4].uDataType == QCBOR_TYPE_FALSE) {
+        pE->bTurboCharged = false;
+    } else {
+        return EngineProtocolerror;
+    }
+
+
+    /* Must check error before referencing pE->uNumCylinders to be
+       sure it is valid. If any of the above errored, it won't be
+       valid. */
+    uErr = QCBORDecode_GetError(&DecodeCtx);
+    if(uErr != QCBOR_SUCCESS) {
+        goto Done;
+    }
+
+    if(pE->uNumCylinders > MAX_CYLINDERS) {
+        return TooManyCylinders;
+    }
+
+    QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
+    for(int64_t i = 0; i < pE->uNumCylinders; i++) {
+        QCBORDecode_GetDouble(&DecodeCtx, &(pE->cylinders[i].uMeasuredCompression));
+    }
+    QCBORDecode_ExitArray(&DecodeCtx);
+    QCBORDecode_ExitMap(&DecodeCtx);
+
+    /* Catch the remainder of errors here */
+    uErr = QCBORDecode_Finish(&DecodeCtx);
+
+Done:
+    return ConvertError(uErr);
+}
+
+#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE_FAST */
+
+
+#ifndef EXAMPLE_DISABLE_BASIC_DECODE
+/**
+ @brief Check the type and lable of an item.
+
+ @param[in] szLabel     The expected string label.
+ @param[in] uQCBORType  The expected type or @c QCBOR_TYPE_ANY.
+ @param[in] pItem       The item to check.
+
+ @retval QCBOR_ERR_LABEL_NOT_FOUND  The label doesn't match.
+ @retval QCBOR_ERR_UNEXPECTED_TYPE  The label matches, but the type is
+                                    not as expected.
+ @retval QCBOR_SUCCESS              Both label and type match.
+ */
+QCBORError CheckLabelAndType(const char *szLabel, uint8_t uQCBORType, const QCBORItem *pItem)
+{
+    if(pItem->uLabelType != QCBOR_TYPE_TEXT_STRING) {
+        return QCBOR_ERR_LABEL_NOT_FOUND;
+    }
+
+    UsefulBufC Label = UsefulBuf_FromSZ(szLabel);
+
+    if(UsefulBuf_Compare(Label, pItem->label.string)) {
+        return QCBOR_ERR_LABEL_NOT_FOUND;
+    }
+
+    if(pItem->uDataType != uQCBORType && uQCBORType != QCBOR_TYPE_ANY) {
+        return QCBOR_ERR_UNEXPECTED_TYPE;
+    }
+
+    return QCBOR_SUCCESS;
+}
+
+
+/**
+ @brief Decode the array of engine cylinders.
+
+ @param[in] pDecodeCtx  The decode context from which to get items.
+ @param[out] pE         The structure filled in from the decoding.
+ @param[in] pItem       The data item that is the start of the array.
+
+ @return Either @ref EngineSuccess or an error.
+
+ This always consumes the whole array. If it has the wrong number of
+ items in it, an error is returned.
+ */
+EngineDecodeErrors DecodeCylinders(QCBORDecodeContext *pDecodeCtx,
+                                   CarEngine          *pE,
+                                   const QCBORItem    *pItem)
+{
+    int i = 0;
+    QCBORItem Item;
+
+    /* Loop getting all the items in the array. This uses nesting
+       level to detect the end so it works for both definite and
+       indefinite length arrays. */
+    do {
+        QCBORError uErr;
+
+        uErr = QCBORDecode_GetNext(pDecodeCtx, &Item);
+        if(uErr != QCBOR_SUCCESS) {
+            return CBORNotWellFormed;
+        }
+        if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
+            return CBORNotWellFormed;
+        }
+
+        if(i < MAX_CYLINDERS) {
+            pE->cylinders[i].uMeasuredCompression = Item.val.dfnum;
+            i++;
+        }
+
+    } while (Item.uNextNestLevel == pItem->uNextNestLevel);
+
+    if(i != pE->uNumCylinders) {
+        return WrongNumberOfCylinders;
+    } else {
+        return EngineSuccess;
+    }
+}
+
+
+/**
+ @brief Engine decode without spiffy decode.
+
+ @param[in] EncodedEngine  Pointer and length of CBOR-encoded engine.
+ @param[out] pE            The structure filled in from the decoding.
+
+ @return The decode error or success.
+
+ This is the third implementation of engine decoding, again
+ implementing the same functionality as DecodeEngineSpiffy() and
+ DecodeEngineSpiffyFaster().
+
+ This version of the deocde is the most complex, but uses
+ significantly less code (2-3KB less on 64-bit Intel) from the QCBOR
+ library.  It is also the most CPU-efficient since it does only one
+ pass through the CBOR.
+*/
+EngineDecodeErrors DecodeEngineBasic(UsefulBufC EncodedEngine, CarEngine *pE)
+{
+    QCBORDecodeContext DecodeCtx;
+
+    QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
+
+    QCBORItem Item;
+    QCBORError uErr;
+    EngineDecodeErrors uReturn;
+
+
+    uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
+    if(uErr != QCBOR_SUCCESS) {
+        uReturn = CBORNotWellFormed;
+        goto Done;
+    }
+    if(Item.uDataType != QCBOR_TYPE_MAP) {
+        uReturn = CBORNotWellFormed;
+        goto Done;
+    }
+
+    while(1) {
+        uErr = QCBORDecode_GetNext(&DecodeCtx, &Item);
+        if(uErr != QCBOR_SUCCESS) {
+            if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
+                break; /* Non-error exit from the loop */
+            } else {
+                uReturn = CBORNotWellFormed;
+                goto Done;
+            }
+        }
+
+        uErr = CheckLabelAndType("Manufacturer", QCBOR_TYPE_TEXT_STRING, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            pE->Manufacturer = Item.val.string;
+            continue;
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            /* Maunfacturer field missing or badly formed */
+            return EngineProtocolerror;
+        } /* continue on and try for another match */
+
+        uErr = CheckLabelAndType("NumCylinders", QCBOR_TYPE_INT64, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            if(Item.val.int64 > MAX_CYLINDERS) {
+                return TooManyCylinders;
+            } else {
+                pE->uNumCylinders = (uint8_t)Item.val.int64;
+                continue;
+            }
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            /* NumCylinders field missing or badly formed */
+            return EngineProtocolerror;
+        } /* continue on and try for another match */
+
+        uErr = CheckLabelAndType("Cylinders", QCBOR_TYPE_ARRAY, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            DecodeCylinders(&DecodeCtx, pE, &Item);
+            continue;
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            return EngineProtocolerror;
+        }
+
+        uErr = CheckLabelAndType("Displacement", QCBOR_TYPE_INT64, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            pE->uDisplacement = Item.val.int64;
+            continue;
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            return EngineProtocolerror;
+        }
+
+        uErr = CheckLabelAndType("Horsepower", QCBOR_TYPE_INT64, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            pE->uHorsePower = Item.val.int64;
+            continue;
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            return EngineProtocolerror;
+        }
+
+        uErr = CheckLabelAndType("DesignedCompression", QCBOR_TYPE_DOUBLE, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            pE->dDesignedCompresion = Item.val.dfnum;
+            continue;
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            return EngineProtocolerror;
+        }
+
+        uErr = CheckLabelAndType("Turbo", QCBOR_TYPE_ANY, &Item);
+        if(uErr == QCBOR_SUCCESS) {
+            if(Item.uDataType == QCBOR_TYPE_TRUE) {
+                pE->bTurboCharged = true;
+            } else if(Item.uDataType == QCBOR_TYPE_FALSE) {
+                pE->bTurboCharged = false;
+            } else {
+                return EngineProtocolerror;
+            }
+            continue;
+        } else if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+            return EngineProtocolerror;
+        }
+
+        /* Some label data item that is not known (could just ignore
+           extras data items) */
+        return EngineProtocolerror;
+    }
+    uReturn = EngineSuccess;
+
+    /* Catch the remainder of errors here */
+    uErr = QCBORDecode_Finish(&DecodeCtx);
+    if(uErr) {
+        uReturn = ConvertError(uErr);
+    }
+
+
+
+Done:
+    return uReturn;
+}
+
+#endif /* EXAMPLE_DISABLE_BASIC_DECODE */
+
+
+int32_t RunQCborExample()
+{
+   CarEngine               E, DecodedEngine;
+   MakeUsefulBufOnStack(   EngineBuffer, 300);
+   UsefulBufC              EncodedEngine;
+
+   MakeUsefulBufOnStack(   InDefEngineBuffer, 300);
+   UsefulBufC              InDefEncodedEngine;
+
+   EngineDecodeErrors      uErr;
+
+   EngineInit(&E);
+
+#ifndef EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE
+   EncodedEngine = EncodeEngineDefiniteLength(&E, EngineBuffer);
+   printf("Definite Length Engine Encoded in %zu bytes\n", EncodedEngine.len);
+#endif /* EXAMPLE_DISABLE_DEFINITE_LENGTH_ENCODE */
+
+
+#ifndef EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE_ENCODE
+   InDefEncodedEngine = EncodeEngineIndefinteLen(&E, InDefEngineBuffer);
+   printf("Indef Engine Encoded in %zu bytes\n", InDefEncodedEngine.len);
+#endif /* EXAMPLE_DISABLE_INDEFINITE_LENGTH_ENCODE_ENCODE */
+
+
+#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE
+   uErr = DecodeEngineSpiffy(EncodedEngine, &DecodedEngine);
+   printf("Spiffy Engine Decode Result: %d\n", uErr);
+
+   if(!EngineCompare(&E, &DecodedEngine)) {
+      printf("Spiffy Engine Decode comparison fail\n");
+   }
+#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE */
+
+#ifndef EXAMPLE_DISABLE_SPIFFY_DECODE_FAST
+   uErr = DecodeEngineSpiffyFaster(EncodedEngine, &DecodedEngine);
+   printf("Faster Spiffy Engine Decode Result: %d\n", uErr);
+
+   if(!EngineCompare(&E, &DecodedEngine)) {
+      printf("Faster Spiffy Engine Decode comparison fail\n");
+   }
+#endif /* EXAMPLE_DISABLE_SPIFFY_DECODE_FAST */
+
+#ifndef EXAMPLE_DISABLE_BASIC_DECODE
+   uErr = DecodeEngineBasic(EncodedEngine, &DecodedEngine);
+   printf("Engine Basic Decode Result: %d\n", uErr);
+
+   if(!EngineCompare(&E, &DecodedEngine)) {
+      printf("Engine Basic Decode comparison fail\n");
+   }
+#endif /* EXAMPLE_DISABLE_BASIC_DECODE */
+
+   printf("\n");
+
+   return 0;
+}
diff --git a/example.h b/example.h
new file mode 100644
index 0000000..b5bcf4b
--- /dev/null
+++ b/example.h
@@ -0,0 +1,20 @@
+/*==============================================================================
+ example.h -- QCBOR encode and decode example
+
+ Copyright (c) 2020, Laurence Lundblade. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+ See BSD-3-Clause license in README.md
+
+ Created on 6/30/20
+ =============================================================================*/
+
+#ifndef qcborExample_h
+#define qcborExample_h
+
+#include <stdint.h>
+
+int32_t RunQCborExample(void);
+
+#endif /* qcborExample_h */
diff --git a/inc/qcbor.h b/inc/qcbor.h
deleted file mode 100644
index 377bade..0000000
--- a/inc/qcbor.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*==============================================================================
- Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2020, Laurence Lundblade.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
- * Neither the name of The Linux Foundation nor the names of its
- contributors, nor the name "Laurence Lundblade" may be used to
- endorse or promote products derived from this software without
- specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
- ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- =============================================================================*/
-
-/**
- * @file qcbor.h
- *
- * Backwards compatibility for includers of qcbor.h (which has been split
- * into four include files).
- */
-
-#include "qcbor/qcbor_encode.h"
-#include "qcbor/qcbor_decode.h"
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 16a53d3..6fe33a1 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -162,6 +162,9 @@
 
 #ifdef __cplusplus
 extern "C" {
+#if 0
+} // Keep editor indention formatting happy
+#endif
 #endif
 
 /**
@@ -1489,6 +1492,37 @@
 static int UsefulInputBuf_GetError(UsefulInputBuf *pUInBuf);
 
 
+/**
+ @brief Gets the input buffer length.
+
+ @param[in] pUInBuf  Pointer to the @ref UsefulInputBuf.
+
+ @return The length of the input buffer.
+
+ This returns the length of th input buffer from UsefulInputBuf_Init()
+ of from UsefulInputBuf_SetBufferLength().
+ */
+static inline size_t UsefulInputBuf_GetBufferLength(UsefulInputBuf *pUInBuf);
+
+
+/**
+ @brief Sets the input buffer length (use with caution)
+
+ @param[in] pUInBuf  Pointer to the @ref UsefulInputBuf.
+
+ This changes the internal remembered length of the input buffer
+ set when UsefulInputBuf_Init() was called. It is used by QCBOR
+ to handle CBOR that is wrapped and embedded in CBOR.
+
+ Since this allows setting the length beyond the length of the
+ original input buffer it allows the overall safety of UsefulInputBug to
+ be undermined. Use it carefully.
+
+ The new length given here should always be equal to or less than
+ the length given when UsefulInputBuf_Init() was called.
+
+ */
+static void UsefulInputBuf_SetBufferLength(UsefulInputBuf *pUInBuf, size_t uNewLen);
 
 
 /*----------------------------------------------------------
@@ -1913,6 +1947,12 @@
 }
 
 
+static inline size_t UsefulInputBuf_GetBufferLength(UsefulInputBuf *pMe)
+{
+    return pMe->UB.len;
+}
+
+
 static inline void UsefulInputBuf_Seek(UsefulInputBuf *pMe, size_t uPos)
 {
    if(uPos > pMe->UB.len) {
@@ -1942,7 +1982,7 @@
       return 0;
    }
 
-   // subtraction can't go neative because of check above
+   // subtraction can't go negative because of check above
    return pMe->UB.len - pMe->cursor;
 }
 
@@ -2125,6 +2165,13 @@
    return pMe->err;
 }
 
+
+static inline void UsefulInputBuf_SetBufferLength(UsefulInputBuf *pMe, size_t uNewLen)
+{
+    pMe->UB.len = uNewLen;
+}
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 9f0a16f..1c76a8a 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -34,6 +34,23 @@
 #ifndef qcbor_common_h
 #define qcbor_common_h
 
+
+/**
+ @file qcbor_common.h
+
+ This define indicates a version of QCBOR that supports spiffy decode,
+ the decode functions found in qcbor_spiffy_decode.h.
+
+ Versions of QCBOR that support spiffy decode are backwards compatible
+ with previous versions, but there are a few minor exceptions such as
+ some aspects of tag handling that are different. This define can be
+ used handle these variances.
+*/
+#define QCBOR_SPIFFY_DECODE
+
+
+
+
 /* Standard CBOR Major type for positive integers of various lengths */
 #define CBOR_MAJOR_TYPE_POSITIVE_INT 0
 
@@ -105,12 +122,12 @@
     mantissa and base-2 scaling factor. See QCBOREncode_AddBigFloat()
     and @ref expAndMantissa. */
 #define CBOR_TAG_BIGFLOAT       5
-/** Tag for COSE format encryption with no recipient
+/** Not Decoded by QCBOR. Tag for COSE format encryption with no recipient
     identification. See [RFC 8152, COSE]
     (https://tools.ietf.org/html/rfc8152). No API is provided for this
     tag. */
 #define CBOR_TAG_COSE_ENCRYPTO 16
-/** Tag for COSE format MAC'd data with no recipient
+/** Not Decoded by QCBOR. Tag for COSE format MAC'd data with no recipient
     identification. See [RFC 8152, COSE]
     (https://tools.ietf.org/html/rfc8152). No API is provided for this
     tag.*/
@@ -139,8 +156,7 @@
     "hex". Call @c QCBOREncode_AddTag(pCtx,CBOR_TAG_ENC_AS_B16) before
     the call to QCBOREncode_AddBytes(). */
 #define CBOR_TAG_ENC_AS_B16    23
-/** Tag to indicate a byte string contains encoded CBOR. No API is
-    provided for this tag. */
+/** See QCBORDecode_EnterBstrWrapped()). */
 #define CBOR_TAG_CBOR          24
 /** See QCBOREncode_AddURI(). */
 #define CBOR_TAG_URI           32
@@ -161,24 +177,35 @@
 /** Tag for COSE format encryption. See [RFC 8152, COSE]
     (https://tools.ietf.org/html/rfc8152). No API is provided for this
     tag. */
+#define CBOR_TAG_CBOR_SEQUENCE 63
+
+
 #define CBOR_TAG_ENCRYPT       96
-/** Tag for COSE format MAC. See [RFC 8152, COSE]
+/** Not Decoded by QCBOR. Tag for COSE format MAC. See [RFC 8152, COSE]
     (https://tools.ietf.org/html/rfc8152). No API is provided for this
     tag. */
 #define CBOR_TAG_MAC           97
-/** Tag for COSE format signed data. See [RFC 8152, COSE]
+/** Not Decoded by QCBOR. Tag for COSE format signed data. See [RFC 8152, COSE]
     (https://tools.ietf.org/html/rfc8152). No API is provided for this
     tag. */
 #define CBOR_TAG_SIGN          98
-/** World geographic coordinates. See ISO 6709, [RFC 5870]
+/** Not Decoded by QCBOR. World geographic coordinates. See ISO 6709, [RFC 5870]
     (https://tools.ietf.org/html/rfc5870) and WGS-84. No API is
     provided for this tag. */
-#define CBOR_TAG_GEO_COORD    103
+#define CBOR_TAG_GEO_COORD     103
+/** Binary MIME.*/
+#define CBOR_TAG_BINARY_MIME   257
 /** The magic number, self-described CBOR. No API is provided for this
     tag. */
-#define CBOR_TAG_CBOR_MAGIC 55799
+#define CBOR_TAG_CBOR_MAGIC    55799
 
-#define CBOR_TAG_NONE  UINT64_MAX
+/** The 16-bit invalid tag from the CBOR tags registry */
+#define CBOR_TAG_INVALID16 0xffff
+/** The 32-bit invalid tag from the CBOR tags registry */
+#define CBOR_TAG_INVALID32 0xffffffff
+/** The 64-bit invalid tag from the CBOR tags registry */
+#define CBOR_TAG_INVALID64 0xffffffffffffffff
+
 
 
 /*
@@ -201,134 +228,247 @@
 
 /**
  Error codes returned by QCBOR Encoder and Decoder.
+ Encode errors are 1..8
+ Decode errors are 9..43
+     Not well-formed errors are 9..16
+     Unrecoverable decode errors are 15..24
+        (partial overlap with not well-formed errors)
+   Other decode errors are 25..43
+
+ The errors are order and grouped intentionally to keep the code size
+ of QCBORDecode_IsNotWellFormedError() and
+ QCBORDecode_IsUnrecoverableError() minimal. Error renumbering may
+ occur in the future when new error codes are added for new QCBOR
+ features.
  */
 typedef enum {
    /** The encode or decode completely correctly. */
    QCBOR_SUCCESS = 0,
 
    /** The buffer provided for the encoded output when doing encoding
-       was too small and the encoded output will not fit. Also, when
-       the buffer given to QCBORDecode_SetMemPool() is too small. */
+       was too small and the encoded output will not fit. */
    QCBOR_ERR_BUFFER_TOO_SMALL = 1,
 
-   /** During encoding or decoding, the array or map nesting was
-       deeper than this implementation can handle. Note that in the
-       interest of code size and memory use, this implementation has a
-       hard limit on array nesting. The limit is defined as the
-       constant @ref QCBOR_MAX_ARRAY_NESTING. */
-   QCBOR_ERR_ARRAY_NESTING_TOO_DEEP = 2,
+   /** During encoding, an attempt to create simple value between 24
+       and 31. */
+   QCBOR_ERR_ENCODE_UNSUPPORTED = 2,
 
-   /** During decoding or encoding, the array or map had too many
-       items in it.  This limit @ref QCBOR_MAX_ITEMS_IN_ARRAY,
-       typically 65,535. */
-   QCBOR_ERR_ARRAY_TOO_LONG = 3,
+   /** During encoding, the length of the encoded CBOR exceeded @c
+       UINT32_MAX. */
+   QCBOR_ERR_BUFFER_TOO_LARGE = 3,
+
+   /** During encoding, the array or map nesting was deeper than this
+       implementation can handle. Note that in the interest of code
+       size and memory use, this implementation has a hard limit on
+       array nesting. The limit is defined as the constant @ref
+       QCBOR_MAX_ARRAY_NESTING. */
+   QCBOR_ERR_ARRAY_NESTING_TOO_DEEP = 4,
+
+   /** During encoding, @c QCBOREncode_CloseXxx() called with a
+       different type than is currently open.  */
+   QCBOR_ERR_CLOSE_MISMATCH = 5,
+
+   /** During encoding, the array or map had too many items in it.
+       This limit @ref QCBOR_MAX_ITEMS_IN_ARRAY, typically 65,535. */
+   QCBOR_ERR_ARRAY_TOO_LONG = 6,
 
    /** During encoding, more arrays or maps were closed than
        opened. This is a coding error on the part of the caller of the
        encoder. */
-   QCBOR_ERR_TOO_MANY_CLOSES = 4,
+   QCBOR_ERR_TOO_MANY_CLOSES = 7,
+
+   /** During encoding the number of array or map opens was not
+       matched by the number of closes. */
+   QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN = 8,
+
+#define QCBOR_START_OF_NOT_WELL_FORMED_ERRORS 9
+
+   /** During decoding, the CBOR is not well-formed because a simple
+       value between 0 and 31 is encoded in a two-byte integer rather
+       than one. */
+   QCBOR_ERR_BAD_TYPE_7 = 9,
+
+   /** During decoding, returned by QCBORDecode_Finish() if all the
+       inputs bytes have not been consumed. This is considered not
+       well-formed. */
+   QCBOR_ERR_EXTRA_BYTES = 10,
 
    /** During decoding, some CBOR construct was encountered that this
        decoder doesn't support, primarily this is the reserved
-       additional info values, 28 through 30. During encoding,
-       an attempt to create simple value between 24 and 31. */
-   QCBOR_ERR_UNSUPPORTED = 5,
+       additional info values, 28 through 30. The CBOR is not
+       well-formed.*/
+   QCBOR_ERR_UNSUPPORTED = 11,
+
+   /** During decoding, the an array or map was not fully consumed.
+       Returned by QCBORDecode_Finish(). The CBOR is not
+       well-formed. */
+   QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED = 12,
+
+   /** During decoding, an integer type is encoded with a bad length
+       (that of an indefinite length string). The CBOR is not-well
+       formed. */
+   QCBOR_ERR_BAD_INT = 13,
+
+#define QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS 14
+
+   /** During decoding, one of the chunks in an indefinite-length
+       string is not of the type of the start of the string.  The CBOR
+       is not well-formed.  This error makes no further decoding
+       possible. */
+   QCBOR_ERR_INDEFINITE_STRING_CHUNK = 14,
 
    /** During decoding, hit the end of the given data to decode. For
        example, a byte string of 100 bytes was expected, but the end
        of the input was hit before finding those 100 bytes.  Corrupted
        CBOR input will often result in this error. See also @ref
-       QCBOR_ERR_NO_MORE_ITEMS.
-     */
-   QCBOR_ERR_HIT_END = 6,
+       QCBOR_ERR_NO_MORE_ITEMS. The CBOR is not well-formed.  This
+       error makes no further decoding possible. */
+   QCBOR_ERR_HIT_END = 15,
 
-   /** During encoding, the length of the encoded CBOR exceeded @c
+   /** During decoding, a break occurred outside an indefinite-length
+       item. The CBOR is not well-formed. This error makes no further
+       decoding possible. */
+   QCBOR_ERR_BAD_BREAK = 16,
+
+#define QCBOR_END_OF_NOT_WELL_FORMED_ERRORS 16
+
+   /** During decoding, the input is too large. It is greater than
+       QCBOR_MAX_DECODE_INPUT_SIZE. This is an implementation limit.
+       This error makes no further decoding possible. */
+   QCBOR_ERR_INPUT_TOO_LARGE = 17,
+
+   /** During decoding, the array or map nesting was deeper than this
+       implementation can handle. Note that in the interest of code
+       size and memory use, this implementation has a hard limit on
+       array nesting. The limit is defined as the constant @ref
+       QCBOR_MAX_ARRAY_NESTING. This error makes no further decoding
+       possible. */
+   QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP = 18,
+
+   /** During decoding, the array or map had too many items in it.
+       This limit @ref QCBOR_MAX_ITEMS_IN_ARRAY, typically 65,534,
+       UINT16_MAX - 1. This error makes no further decoding
+       possible. */
+   QCBOR_ERR_ARRAY_DECODE_TOO_LONG = 19,
+
+   /** When decoding, a string's size is greater than what a size_t
+       can hold less 4. In all but some very strange situations this
+       is because of corrupt input CBOR and should be treated as
+       such. The strange situation is a CPU with a very small size_t
+       (e.g., a 16-bit CPU) and a large string (e.g., > 65KB). This
+       error makes no further decoding possible. */
+   QCBOR_ERR_STRING_TOO_LONG = 20,
+
+   /** Something is wrong with a decimal fraction or bigfloat such as
+       it not consisting of an array with two integers. This error
+       makes no further decoding possible. */
+   QCBOR_ERR_BAD_EXP_AND_MANTISSA = 21,
+
+   /** Unable to decode an indefinite-length string because no string
+       allocator was configured. See QCBORDecode_SetMemPool() or
+       QCBORDecode_SetUpAllocator().  This error makes no further
+       decoding possible.*/
+   QCBOR_ERR_NO_STRING_ALLOCATOR = 22,
+
+   /** Error allocating space for a string, usually for an
+       indefinite-length string. This error makes no further decoding
+       possible. */
+   QCBOR_ERR_STRING_ALLOCATE = 23,
+
+#define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 23
+
+   /** More than @ref QCBOR_MAX_TAGS_PER_ITEM tags encounterd for a
+       CBOR ITEM.  @ref QCBOR_MAX_TAGS_PER_ITEM is a limit of this
+       implementation.  During decoding, too many tags in the
+       caller-configured tag list, or not enough space in @ref
+       QCBORTagListOut. This error makes no further decoding
+       possible.  */
+   QCBOR_ERR_TOO_MANY_TAGS = 24,
+
+   /** During decoding, the type of the label for a map entry is not
+       one that can be handled in the current decoding mode. Typically
+       this is because a label is not an intger or a string. This is
+       an implemation limit. */
+   QCBOR_ERR_MAP_LABEL_TYPE = 25,
+
+   /** When decodeing for a specific type, the type was not was
+       expected.  */
+   QCBOR_ERR_UNEXPECTED_TYPE = 26,
+
+   /** This occurs when decoding one of the tags that QCBOR processed
+       internally.  The content of a tag was of the wrong type. (They
+       were known as "Optional Tags" in RFC 7049. */
+   QCBOR_ERR_BAD_OPT_TAG = 27,
+
+   /** Duplicate label in map detected */
+   QCBOR_ERR_DUPLICATE_LABEL = 28,
+
+   /** During decoding, the buffer given to QCBORDecode_SetMemPool()
+       is either too small, smaller than
+       QCBOR_DECODE_MIN_MEM_POOL_SIZE or too large, larger than
        UINT32_MAX. */
-   QCBOR_ERR_BUFFER_TOO_LARGE = 7,
+   QCBOR_ERR_MEM_POOL_SIZE = 29,
 
    /** During decoding, an integer smaller than INT64_MIN was received
        (CBOR can represent integers smaller than INT64_MIN, but C
        cannot). */
-   QCBOR_ERR_INT_OVERFLOW = 8,
-
-   /** During decoding, the label for a map entry is bad. What causes
-       this error depends on the decoding mode. */
-   QCBOR_ERR_MAP_LABEL_TYPE = 9,
-
-   /** During encoding or decoding, the number of array or map opens
-       was not matched by the number of closes. */
-   QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN = 10,
+   QCBOR_ERR_INT_OVERFLOW = 30,
 
    /** During decoding, a date greater than +- 292 billion years from
-       Jan 1 1970 encountered during parsing. */
-   QCBOR_ERR_DATE_OVERFLOW = 11,
+       Jan 1 1970 encountered during parsing. This is an
+       implementation limit. */
+   QCBOR_ERR_DATE_OVERFLOW = 31,
 
-   /** During decoding, the CBOR is not valid, primarily a simple type
-      is encoded in a prohibited way. */
-   QCBOR_ERR_BAD_TYPE_7 = 12,
-
-   /** Optional tagging that doesn't make sense (an integer is tagged
-       as a date string) or can't be handled. */
-   QCBOR_ERR_BAD_OPT_TAG = 13,
-
-   /** Returned by QCBORDecode_Finish() if all the inputs bytes have
-       not been consumed. */
-   QCBOR_ERR_EXTRA_BYTES = 14,
-
-   /** During encoding, @c QCBOREncode_CloseXxx() called with a
-       different type than is currently open. */
-   QCBOR_ERR_CLOSE_MISMATCH = 15,
-
-   /** Unable to decode an indefinite-length string because no string
-       allocator was configured. See QCBORDecode_SetMemPool() or
-       QCBORDecode_SetUpAllocator(). */
-   QCBOR_ERR_NO_STRING_ALLOCATOR = 16,
-
-   /** One of the chunks in an indefinite-length string is not of the
-       type of the start of the string. */
-   QCBOR_ERR_INDEFINITE_STRING_CHUNK = 17,
-
-   /** Error allocating space for a string, usually for an
-       indefinite-length string. */
-   QCBOR_ERR_STRING_ALLOCATE = 18,
-
-   /** During decoding, a break occurred outside an indefinite-length
-       item. */
-   QCBOR_ERR_BAD_BREAK = 19,
-
-   /** During decoding, too many tags in the caller-configured tag
-       list, or not enough space in @ref QCBORTagListOut. */
-   QCBOR_ERR_TOO_MANY_TAGS = 20,
-
-   /** An integer type is encoded with a bad length (an indefinite length) */
-   QCBOR_ERR_BAD_INT = 21,
+   /** During decoding, @c QCBORDecode_ExitXxx() was called for a
+       different type than @c QCBORDecode_EnterXxx(). */
+   QCBOR_ERR_EXIT_MISMATCH = 32,
 
    /** All well-formed data items have been consumed and there are no
-       more. If parsing a CBOR stream this indicates the non-error
-       end of the stream. If parsing a CBOR stream / sequence, this
-       probably indicates that some data items expected are not present.
-       See also @ref QCBOR_ERR_HIT_END. */
-   QCBOR_ERR_NO_MORE_ITEMS = 22,
+       more. If parsing a CBOR stream this indicates the non-error end
+       of the stream. If parsing a CBOR stream / sequence, this
+       probably indicates that some data items expected are not
+       present.  See also @ref QCBOR_ERR_HIT_END. */
+   QCBOR_ERR_NO_MORE_ITEMS = 33,
 
-   /** Something is wrong with a decimal fraction or bigfloat such as
-    it not consisting of an array with two integers */
-   QCBOR_ERR_BAD_EXP_AND_MANTISSA = 23,
+   /** When finding an item by lablel, an item with the requested label
+       was not found. */
+   QCBOR_ERR_LABEL_NOT_FOUND = 34,
 
-   /** When decoding, a string's size is greater than size_t. In all but some
-       very strange situations this is because of corrupt input CBOR and
-       should be treated as such. The strange situation is a CPU with a very
-       small size_t (e.g., a 16-bit CPU) and a large string (e.g., > 65KB).
-    */
-    QCBOR_ERR_STRING_TOO_LONG = 24,
+   /** Number conversion failed because of sign. For example a
+       negative int64_t can't be converted to a uint64_t */
+   QCBOR_ERR_NUMBER_SIGN_CONVERSION = 35,
 
-    /** Decoding of floating-point epoch dates is unsupported and a
-        floating-point date was encountered by the decoder. */
-    QCBOR_ERR_FLOAT_DATE_UNSUPPORTED = 25,
+   /** When converting a decoded number, the value is too large or to
+       small for the conversion target */
+   QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW = 36,
 
-    /** Support for half-precision float decoding is disabled. */
-    QCBOR_ERR_HALF_PRECISION_UNSUPPORTED = 26,
+   /** Trying to get an item by label when a map has not been
+       entered. */
+   QCBOR_ERR_MAP_NOT_ENTERED = 37,
 
-    /* This is stored in uint8_t in places; never add values > 255 */
+   /** A callback indicates processing should not continue for some
+       non-CBOR reason */
+   QCBOR_ERR_CALLBACK_FAIL = 38,
+
+   /** Decoding of floating-point epoch dates is unsupported and a
+       floating-point date was encountered by the decoder. */
+   QCBOR_ERR_FLOAT_DATE_DISABLED = 39,
+
+   /** Support for half-precision float decoding is disabled. */
+   QCBOR_ERR_HALF_PRECISION_DISABLED = 40,
+
+   /** Use of floating-point HW is disabled. This affects all type
+       conversions to and from double and float types. */
+   QCBOR_ERR_HW_FLOAT_DISABLED = 41,
+
+   /** Unable to complete operation because a floating-point value
+       that is a NaN (not a number), that is too large, too small,
+       infinity or -infinity was encountered in encoded CBOR. Usually
+       this because conversion of the float-point value was being
+       attempted. */
+    QCBOR_ERR_FLOAT_EXCEPTION = 42,
+
+   /* This is stored in uint8_t; never add values > 255 */
 } QCBORError;
 
 
@@ -340,7 +480,7 @@
 /**
  The maximum nesting of arrays and maps when encoding or decoding. The
  error @ref QCBOR_ERR_ARRAY_NESTING_TOO_DEEP will be returned on
- encoding of decoding if it is exceeded.
+ encoding or QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP on decoding if it is exceeded.
  */
 #define QCBOR_MAX_ARRAY_NESTING  QCBOR_MAX_ARRAY_NESTING1
 
@@ -354,6 +494,9 @@
 
 
 /**
+ This is deprecated. See QCBORDecode_GetNthTag() and QCBORDecode_GetNthTagOfLast()
+ for tag handling.
+
  The maximum number of tags that can be in @ref QCBORTagListIn and passed to
  QCBORDecode_SetCallerConfiguredTagList()
  */
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 9fa7a40..90a1686 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -49,9 +49,45 @@
 
 
 /**
- @file qcbor_decode.h
+@file qcbor_decode.h
 
- This describes CBOR decoding.
+ @anchor BasicDecode
+ # QCBOR Basic Decode
+
+ This section just discusses decoding assuming familiarity with the
+ general description of this encoder / decoder in section @ref
+ Overview.
+
+ Encoded CBOR can be viewed to have a tree structure where the leaf
+ nodes are non-aggregate types like integers and strings and the
+ intermediate nodes are either arrays or maps. Fundamentally, all CBOR
+ decoding is a pre-order traversal of the tree. Calling
+ QCBORDecode_GetNext() repeatedly will perform this. It is possible to
+ decode any CBOR by only calling QCBORDecode_GetNext().
+
+ QCBORDecode_GetNext() returns a 56 byte structure called
+ @ref QCBORItem that describes the decoded item including
+ - The data itself, integer, string, floating-point number...
+ - The label if present
+ - Unprocessed tags
+ - Nesting level
+ - Allocation type (primarily of interest for indefinite length strings)
+
+ For strings, this structure contains a pointer and length
+ back into the original data.
+
+ All of the tags that QCBOR supports directly are decoded into a
+ representation in @ref QCBORItem.
+
+ A string allocator must be used when decoding indefinite length
+ strings. See QCBORDecode_SetMemPool() or
+ QCBORDecode_SetUpAllocator(). @ref QCBORItem indicates if a string
+ was allocated with the string allocator.
+
+ This pre-order traversal gives natural decoding of arrays where the
+ array members are taken in order, but does not give natural decoding
+ of maps where access by label is usually preferred.  See
+ @ref SpiffyDecode for APIs to search maps by label and much more.
 */
 
 /**
@@ -67,13 +103,26 @@
    /* This is stored in uint8_t in places; never add values > 255 */
 } QCBORDecodeMode;
 
+/**
+ The maximum size of input to the decoder. Slightly less than UINT32_MAX
+ to make room for some special indicator values.
+ */
+#define QCBOR_MAX_DECODE_INPUT_SIZE (UINT32_MAX - 2)
 
+/**
+ The maximum number of tags that may occur on an individual nested
+ item. Typically 4.
+ */
+#define QCBOR_MAX_TAGS_PER_ITEM QCBOR_MAX_TAGS_PER_ITEM1
 
 
 
 /* Do not renumber these. Code depends on some of these values. */
 /** The data type is unknown, unset or invalid. */
 #define QCBOR_TYPE_NONE           0
+/** Never used in QCBORItem. Used by functions that match QCBOR types. */
+#define QCBOR_TYPE_ANY            1
+
 /** Type for an integer that decoded either between @c INT64_MIN and
     @c INT32_MIN or @c INT32_MAX and @c INT64_MAX. Data is in member
     @c val.int64. */
@@ -81,15 +130,15 @@
 /** Type for an integer that decoded to a more than @c INT64_MAX and
      @c UINT64_MAX.  Data is in member @c val.uint64. */
 #define QCBOR_TYPE_UINT64         3
-/** Type for an array. The number of items in the array is in @c
-    val.uCount. */
+/** Type for an array. See comments on @c val.uCount. */
 #define QCBOR_TYPE_ARRAY          4
-/** Type for a map; number of items in map is in @c val.uCount. */
+/** Type for a map. See comments on @c val.uCount. */
 #define QCBOR_TYPE_MAP            5
 /** Type for a buffer full of bytes. Data is in @c val.string. */
 #define QCBOR_TYPE_BYTE_STRING    6
-/** Type for a UTF-8 string. It is not NULL-terminated. Data is in @c
-    val.string.  */
+/** Type for a UTF-8 string. It is not NULL-terminated. See
+    QCBOREncode_AddText() for a discussion of line endings in CBOR. Data
+    is in @c val.string.  */
 #define QCBOR_TYPE_TEXT_STRING    7
 /** Type for a positive big number. Data is in @c val.bignum, a
     pointer and a length. */
@@ -124,17 +173,17 @@
 /** A floating-point number made of base-2 exponent and integer
     mantissa.  See @ref expAndMantissa and
     QCBOREncode_AddBigFloat(). */
-#define QCBOR_TYPE_BIGFLOAT      17
+#define QCBOR_TYPE_BIGFLOAT                    17
 
 /** A floating-point number made of base-2 exponent and positive big
     number mantissa.  See @ref expAndMantissa and
     QCBOREncode_AddBigFloatBigNum(). */
-#define QCBOR_TYPE_BIGFLOAT_POS_BIGNUM      18
+#define QCBOR_TYPE_BIGFLOAT_POS_BIGNUM         18
 
 /** A floating-point number made of base-2 exponent and negative big
     number mantissa.  See @ref expAndMantissa and
     QCBOREncode_AddBigFloatBigNum(). */
-#define QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM      19
+#define QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM         19
 
 /** Type for the value false. */
 #define QCBOR_TYPE_FALSE         20
@@ -148,35 +197,77 @@
 #define QCBOR_TYPE_FLOAT         26
 /** Type for a double floating-point number. Data is in @c val.double. */
 #define QCBOR_TYPE_DOUBLE        27
-/** For @ref QCBOR_DECODE_MODE_MAP_AS_ARRAY decode mode, a map that is
-     being traversed as an array. See QCBORDecode_Init() */
-#define QCBOR_TYPE_MAP_AS_ARRAY  32
 
 #define QCBOR_TYPE_BREAK         31 // Used internally; never returned
 
-#define QCBOR_TYPE_OPTTAG       254 // Used internally; never returned
+/** For @ref QCBOR_DECODE_MODE_MAP_AS_ARRAY decode mode, a map that is
+    being traversed as an array. See QCBORDecode_Init() */
+#define QCBOR_TYPE_MAP_AS_ARRAY  32
+
+/* Start of QCBOR types that are defined as the CBOR tag + 12 */
+
+/** Encoded CBOR that is wrapped in a byte string. Often used when the
+    CBOR is to be hashed for signing or HMAC. See also @ref
+    QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE. Data is in @c val.string. */
+#define QBCOR_TYPE_WRAPPED_CBOR  36
+
+/** A URI as defined in RFC 3986.  Data is in @c val.string. */
+#define QCBOR_TYPE_URI           44
+
+/** Text is base64 URL encoded in RFC 4648.  The base64 encoding is
+    NOT removed. Data is in @c val.string. */
+#define QCBOR_TYPE_BASE64URL     45
+
+/** Text is base64 encoded in RFC 4648.  The base64 encoding is NOT
+    removed. Data is in @c val.string. */
+#define QCBOR_TYPE_BASE64        46
+
+/** PERL-compatible regular expression. Data is in @c val.string. */
+#define QCBOR_TYPE_REGEX         47
+
+/** Non-binary MIME per RFC 2045.  See also @ref
+    QCBOR_TYPE_BINARY_MIME. Data is in @c val.string. */
+#define QCBOR_TYPE_MIME          48
+
+/** Binary UUID per RFC 4122.  Data is in @c val.string. */
+#define QCBOR_TYPE_UUID          49
+
+/** A CBOR sequence per RFC 8742. See also @ ref
+    QBCOR_TYPE_WRAPPED_CBOR.  Data is in @c val.string. */
+#define QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE  75
+
+/* End of QCBOR types that are CBOR tag + 12 */
+
+/** Binary MIME per RFC 2045. See also @ref QCBOR_TYPE_MIME. Data is
+    in @c val.string. */
+#define QCBOR_TYPE_BINARY_MIME   76
+
+#define QCBOR_TYPE_TAG        254 // Used internally; never returned
+
+#define QCBOR_TYPE_OPTTAG   QCBOR_TYPE_TAG // Depricated in favor of QCBOR_TYPE_TAG
 
 
 
-/*
- Approx Size of this:
-   8 + 8 + 1 + 1 + 1 + (1 padding) + (4 padding) = 24 for first part
-                                                  (20 on a 32-bit machine)
-   16 bytes for the val union
-   16 bytes for label union
-   total = 56 bytes (52 bytes on 32-bit machine)
+/**
+ The largest value in @c utags that is unmapped and can be used without
+ mapping it through QCBORDecode_GetNthTag().
  */
+#define QCBOR_LAST_UNMAPPED_TAG (CBOR_TAG_INVALID16 - QCBOR_NUM_MAPPED_TAGS - 1)
+
 
 /**
  The main data structure that holds the type, value and other info for
  a decoded item returned by QCBORDecode_GetNext() and
- QCBORDecode_GetNextWithTags().
+ and methods.
+
+ This size of this may vary by compiler but is roughly 56 bytes on
+ a 64-bit CPU and 52 bytes on a 32-bit CPU.
  */
 typedef struct _QCBORItem {
    /** Tells what element of the @c val union to use. One of @c
        QCBOR_TYPE_XXXX */
    uint8_t  uDataType;
-   /** How deep the nesting from arrays and maps are. 0 is the top
+   /** How deep the nesting from arrays and maps is. 0 is the top
        level with no arrays or maps entered. */
    uint8_t  uNestingLevel;
     /** Tells what element of the label union to use. */
@@ -186,8 +277,9 @@
    uint8_t  uDataAlloc;
    /** Like @c uDataAlloc, but for label. */
    uint8_t  uLabelAlloc;
-   /** If not equal to @c uNestingLevel, this item closed out at least
-       one map/array */
+   /** The nesting level of the next item after this one.  If less
+       than @c uNestingLevel, this item was the last one in an arry or
+       map and closed out at least one nesting level */
    uint8_t  uNextNestLevel;
 
    /** The union holding the item's value. Select union member based
@@ -203,13 +295,18 @@
       /** The "value" for @c uDataType @ref QCBOR_TYPE_ARRAY or @ref
           QCBOR_TYPE_MAP -- the number of items in the array or map.
           It is @c UINT16_MAX when decoding indefinite-lengths maps
+          and arrays. Detection of the end of a map or array is
+          best done with uNextLevel and uNextNestLevel so as to
+          work for both definite and indefinite length maps
           and arrays. */
       uint16_t    uCount;
       /** The value for @c uDataType @ref QCBOR_TYPE_DOUBLE. */
       double      dfnum;
       /** The value for @c uDataType @ref QCBOR_TYPE_FLOAT. */
       float       fnum;
-      /** The value for @c uDataType @ref QCBOR_TYPE_DATE_EPOCH. */
+      /** The value for @c uDataType @ref QCBOR_TYPE_DATE_EPOCH.
+          Floating-point dates that are NaN, +Inifinity or -Inifinity
+          result in the @ref QCBOR_ERR_DATE_OVERFLOW error. */
       struct {
          int64_t  nSeconds;
          double   fSecondsFraction;
@@ -253,6 +350,7 @@
       } expAndMantissa;
 #endif
       uint64_t    uTagV;  // Used internally during decoding
+
    } val;
 
    /** Union holding the different label types selected based on @c
@@ -267,12 +365,34 @@
       uint64_t    uint64;
    } label;
 
-   /** Bit indicating which tags (major type 6) on this item. See
-       QCBORDecode_IsTagged().  */
-   uint64_t uTagBits;
+   /** The tags on the item.  Tags nest, so index 0 in the array is
+       the tag on the data item itself, index 1 is the tag that
+       applies to the tag in index 0. The end of the list is indicated
+       by @ref CBOR_TAG_INVALID16
+
+       Tag nesting is uncommon and rarely deep. This implementation
+       only allows nesting to a depth of @ref QCBOR_MAX_TAGS_PER_ITEM,
+       usually 4.
+
+       Tags in the array below and equal to @ref
+       QCBOR_LAST_UNMAPPED_TAG are unmapped and can be used
+       directly. Tags above this must be be translated through
+       QCBORDecode_GetNthTag().
+
+       See also the large number of QCBORDecode_GetXxxx() functions in
+       qcbor_spiffy_decode.h for a way to decode tagged types without
+       having to reference this array.
+    */
+   uint16_t uTags[QCBOR_MAX_TAGS_PER_ITEM];
 
 } QCBORItem;
 
+/**
+   An array or map's length is indefinite when it has this value.
+ */
+#define QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH UINT16_MAX
+
+
 
 
 /**
@@ -521,24 +641,13 @@
                                 bool bAllStrings);
 
 /**
- @brief Configure list of caller-selected tags to be recognized.
+ @brief Deprecated -- 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 @c CBOR_TAG_XXX.
-
- The list pointed to by @c pTagList must persist during decoding.  No
- copy of it is made.
-
- The maximum number of tags that can be added is @ref
- QCBOR_MAX_CUSTOM_TAGS.  If a list larger than this is given, the
- error will be returned when QCBORDecode_GetNext() is called, not
- here.
-
- See description of @ref QCBORTagListIn.
+ Tag handling has been revised and it is no longer ncessary to use this.
+ See QCBORDecode_GetNthTag().
  */
 void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *pCtx, const QCBORTagListIn *pTagList);
 
@@ -576,7 +685,7 @@
 
  @retval QCBOR_ERR_BAD_OPT_TAG     Invalid CBOR, tag on wrong type.
 
- @retval QCBOR_ERR_ARRAY_TOO_LONG  Implementation limit, array or map
+ @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG  Implementation limit, array or map
                                    too long.
 
  @retval QCBOR_ERR_INT_OVERFLOW    Implementation limit, negative
@@ -585,7 +694,7 @@
  @retval QCBOR_ERR_DATE_OVERFLOW   Implementation limit, date larger
                                    than can be handled.
 
- @retval QCBOR_ERR_ARRAY_NESTING_TOO_DEEP  Implementation limit, nesting
+ @retval QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP  Implementation limit, nesting
                                            too deep.
 
  @retval QCBOR_ERR_STRING_ALLOCATE Resource exhaustion, string allocator
@@ -684,36 +793,23 @@
  map or array has been encountered. This works the same for both
  definite and indefinite-length arrays.
 
- This decoder support CBOR type 6 tagging. The decoding of particular
- given tag value may be supported in one of three different ways.
+ QCBOR will automatically decode all the tags defined in RFC 7049
+ plus a few more. They will show up in a QCBORItem as QCBOR types
+ like QCBOR_TYPE_POS_BIGNUM.
 
- First, some common tags are fully and transparently supported by
- automatically decoding them and returning them in a @ref QCBORItem.
- These tags have a @c QCBOR_TYPE_XXX associated with them and manifest
- pretty much the same as a standard CBOR type. @ref
- QCBOR_TYPE_DATE_EPOCH and the @c epochDate member of @ref QCBORItem
- is an example.
+ Most tags with a CBOR_TAG_XXX define in qcbor_common.h like @ref
+ CBOR_TAG_DATE_STRING are automaticlly decoded by QCBOR. Those that
+ are defined but not decoded are so noted.
 
- Second are tags that are automatically recognized, but not decoded.
- These are tags that have a @c \#define of the form @c CBOR_TAG_XXX.
- These are recorded in the @c uTagBits member of @ref QCBORItem. There
- is an internal table that maps each bit to a particular tag value
- allowing up to 64 tags on an individual item to be reported (it is
- rare to have more than one or two). To find out if a particular tag
- value is set call QCBORDecode_IsTagged() on the @ref QCBORItem.  See
- also QCBORDecode_GetNextWithTags().
+ Tags that are not decoded by QCBOR will be identified and recorded in
+ a QCBORItem. Use QCBORDecode_GetNthTag() to get them. Only @ref
+ QCBOR_MAX_TAGS_PER_ITEM tags are recorded per item and an error is
+ returned if there are more than that.
 
- Third are tags that are not automatically recognized, because they
- are proprietary, custom or more recently registered with [IANA]
- (https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml). The
- internal mapping table has to be configured to recognize these. Call
- QCBORDecode_SetCallerConfiguredTagList() to do that. Then
- QCBORDecode_IsTagged() will work with them.
-
- The actual decoding of tags supported in the second and third way
- must be handled by the caller. Often this is simply verifying that
- the expected tag is present on a map, byte string or such.  In other
- cases, there might a complicated map structure to decode.
+ Previous versions of QCBOR handled tags in a more complex way using
+ QCBORDecode_SetCallerConfiguredTagList() and
+ QCBORDecode_GetNextWithTags().  This is largely compatible, but
+ imposes the limit of @ref QCBOR_MAX_TAGS_PER_ITEM tags per item
 
  See @ref Tags-Overview for a description of how to go about creating
  custom tags.
@@ -723,8 +819,8 @@
  particular keeping @ref QCBORItem as small as possible.
 
  If any error occurs, \c uDataType and \c uLabelType will be set
- to \ref QCBOR_TYPE_NONE. If there is no need to know the specific
- error, \ref QCBOR_TYPE_NONE can be checked for and the return value
+ to @ref QCBOR_TYPE_NONE. If there is no need to know the specific
+ error, @ref QCBOR_TYPE_NONE can be checked for and the return value
  ignored.
 
  Errors fall in several categories as noted in list above:
@@ -748,55 +844,73 @@
  extra configuration. These are indefinite length strings and maps
  with labels that are not strings or integers. See QCBORDecode_Init().
 
+ This does not set the internal error code or cease to function when
+ it is set. The error returned must always be checked. See also
+ QCBORDecode_VGetNext().
  */
 QCBORError QCBORDecode_GetNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
 
 
 /**
+ @brief QCBORDecode_GetNext() using internal error state error handling.
+
+ @param[in]  pCtx          The decoder context.
+ @param[out] pDecodedItem  Holds the CBOR item just decoded.
+
+ This is the same as QCBORDecode_GetNext() but uses the error handling
+ method of spiffy decode where an internal error is set instead
+ of returning an error. If the internal error is set, this doesn't
+ do anything.
+*/
+void QCBORDecode_VGetNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
+
+
+/**
  @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. See
-                           @ref QCBORTagListOut.
+ the tags on this item. See
+ @ref QCBORTagListOut.
 
  @return See return values for QCBORDecode_GetNext().
 
  @retval QCBOR_ERR_TOO_MANY_TAGS  The size of @c pTagList is too small.
 
+ This is retained for backwards compatibility. It is replaced by
+ QCBORDecode_GetNthTag() which can also return all the
+ tags that have been decoded.
+
+ This is not backwards compatibile in two ways. First, it is limited to
+ \ref QCBOR_MAX_TAGS_PER_ITEM items whereas previously
+ it was unlimited. Second, it will not inlucde the tags that QCBOR
+ decodes internally.
+
  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 to implement a CBOR-based
- protocol.  See QCBORDecode_GetNext() for the main description of tag
- decoding.
+ returns the list of tags for the data item in \c pTagList.
 
- Tags will be returned here whether or not they are in the built-in or
- caller-configured tag lists.
+ The 0th tag returned here is the one furthest from the data item. This
+ is opposite the order for QCBORDecode_GetNthTag().
 
- CBOR has no upper bound of limit on the number of tags that can be
- associated with a data item though in practice the number of tags on
- an item will usually be small, perhaps less than five. This will
+ CBOR has no upper bound or limit on the number of tags that can be
+ associated with a data item but in practice the number of tags on
+ an item will usually be small. This will
  return @ref QCBOR_ERR_TOO_MANY_TAGS if the array in @c pTagList is
  too small to hold all the tags for the item.
-
- (This function is separate from QCBORDecode_GetNext() so as to not
- have to make @ref 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 @c uint64_t ).
  */
 QCBORError QCBORDecode_GetNextWithTags(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem, QCBORTagListOut *pTagList);
 
 
 /**
- @brief Determine if a CBOR item was tagged with a particular tag
+ @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, one of @c CBOR_TAG_XXX.
+ @param[in] uTag    The tag to check, one of @c CBOR_TAG_XXX,
+                   for example, @ref CBOR_TAG_DATE_STRING.
 
- @return 1 if it was tagged, 0 if not
+ @return true if it was tagged, false if not
 
  See QCBORDecode_GetNext() for the main description of tag
  handling. For tags that are not fully decoded a bit corresponding to
@@ -812,11 +926,41 @@
  add new tags to the internal list so they can be checked for with
  this function.
  */
-int QCBORDecode_IsTagged(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint64_t uTag);
+bool QCBORDecode_IsTagged(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint64_t uTag);
 
 
 /**
- Check whether all the bytes have been decoded and maps and arrays closed.
+ @brief Returns the tag values for an item.
+
+ @param[in] pCtx    The decoder context.
+ @param[in] pItem The CBOR item to get the tag for.
+ @param[in] uIndex The index of the tag to get.
+
+ @returns The actual nth tag value.
+
+ Up to @ref QCBOR_MAX_TAGS_PER_ITEM are recorded for a decoded CBOR item. If there
+ are more than this, the @ref QCBOR_ERR_TOO_MANY_TAGS error is returned
+ by QCBORDecode_GetNext() and other. This is a limit of this implementation,
+ not of CBOR.
+
+ The 0th tag (@c uIndex 0) is the one that occurs closest to the data item.
+ Tags nest, so the nth tag applies to what ever type
+ is a result of applying the (n-1) tag.
+
+ To reduce memory used by a QCBORItem, this implementation maps
+ all tags larger than UINT16_MAX. This function does the unmapping.
+
+ This returns @ref CBOR_TAG_INVALID64 on all errors or if the nth tag is requested and
+ there is no nth tag. If there are no tags on the item, then
+ requesting the 0th tag will return @ref CBOR_TAG_INVALID64.
+ */
+uint64_t QCBORDecode_GetNthTag(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint32_t uIndex);
+
+
+uint64_t QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pCtx, uint32_t uIndex);
+
+/**
+ @brief Check whether all the bytes have been decoded and maps and arrays closed.
 
  @param[in]  pCtx  The context to check.
 
@@ -855,6 +999,87 @@
 
 
 
+/**
+ @brief Get the decoding error.
+
+ @param[in] pCtx    The decoder context.
+ @return            The decoding error.
+
+ All decoding functions set a saved internal error when they fail.
+ Most decoding functions do not return an error. To know the error,
+ this function must be called.
+
+ The intended use is that decoding functions can be called one
+ after another with no regard to error until either the whole
+ decode is finished or some data returned from the decode must
+ be referenced. This makes implementations of protocols much
+ cleaner and prettier. For simple protocols the only error check
+ may be the return code from QCBORDecode_Finish().
+
+ Once a decoding error has occured most decode functions will do
+ nothing if called. QCBORDecode_GetNext() is an exception.
+
+ The implementation of this is just an inline accessor function
+ so its use adds very little object code.
+
+ Note that no reference to the decoded data should be made until
+ after QCBORDecode_Finish() is called as it will not be valid
+ after a decoding error has occured.
+
+ This will not work for protocols where the expected data items
+ depend on preceding data items existence, type, label or value.
+ In that case call this function to see there is no error
+ before examining data items before QCBORDecode_Finish() is
+ called.
+
+ Some errors, like integer conversion overflow, date string
+ format may not affect the flow of a protocol. The protocol
+ decoder may wish to proceed even if they occur. In that case
+ QCBORDecode_GetAndResetError() may be called after these
+ data items are fetched.
+ */
+static QCBORError QCBORDecode_GetError(QCBORDecodeContext *pCtx);
+
+
+/**
+ @brief Get and reset the decoding error.
+
+ @param[in] pCtx    The decoder context.
+ @returns The decoding error.
+
+ This returns the same as QCBORDecode_GetError() and also
+ resets the error state to @ref QCBOR_SUCCESS.
+ */
+static QCBORError QCBORDecode_GetAndResetError(QCBORDecodeContext *pCtx);
+
+
+/**
+ @brief Whether an error indicates non-well-formed CBOR.
+
+ @param[in] uErr    The decoder context.
+ @return @c true if the error code indicates non-well-formed CBOR.
+ */
+static bool QCBORDecode_IsNotWellFormedError(QCBORError uErr);
+
+
+/**
+ @brief Whether a decoding error is recoverable.
+
+ @param[in] uErr    The decoder context.
+ @return @c true if the error code indicates and uncrecoverable error.
+
+ When an error is unrecoverable, no further decoding of the input is possible.
+ CBOR is a compact format with almost no redundancy so errors like
+ incorrect lengths or array counts are unrecoverable. Unrecoverable
+ errors also occur when certain implementation limits such as the
+ limit on array and map nesting occur.
+
+ The specific errors are a range of the errors in @ref QCBORError.
+ */
+static bool QCBORDecode_IsUnrecoverableError(QCBORError uErr);
+
+
+
 
 /**
  @brief Convert int64_t to smaller integers safely.
@@ -969,6 +1194,50 @@
    return 0;
 }
 
+
+
+
+static inline QCBORError QCBORDecode_GetError(QCBORDecodeContext *pMe)
+{
+    return pMe->uLastError;
+}
+
+static inline QCBORError QCBORDecode_GetAndResetError(QCBORDecodeContext *pMe)
+{
+    const QCBORError uReturn = pMe->uLastError;
+    pMe->uLastError = QCBOR_SUCCESS;
+    return uReturn;
+}
+
+static inline bool QCBORDecode_IsNotWellFormedError(QCBORError uErr)
+{
+   if(uErr >= QCBOR_START_OF_NOT_WELL_FORMED_ERRORS &&
+      uErr <= QCBOR_END_OF_NOT_WELL_FORMED_ERRORS) {
+      return true;
+   } else {
+      return false;
+   }
+}
+
+static inline bool QCBORDecode_IsUnrecoverableError(QCBORError uErr)
+{
+   if(uErr >= QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS &&
+      uErr <= QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS) {
+      return true;
+   } else {
+      return false;
+   }
+}
+
+// A few sanity checks on size constants and special value lenghts
+#if  QCBOR_MAP_OFFSET_CACHE_INVALID < QCBOR_MAX_DECODE_INPUT_SIZE
+#error QCBOR_MAP_OFFSET_CACHE_INVALID is too large
+#endif
+
+#if QCBOR_NON_BOUNDED_OFFSET < QCBOR_MAX_DECODE_INPUT_SIZE
+#error QCBOR_NON_BOUNDED_OFFSET is too large
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 49bdaff..ae5027a 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -51,12 +51,20 @@
 /**
  @file qcbor_encode.h
 
- Q C B O R   E n c o d e / D e c o d e
+ @anchor Overview
+
+ # QCBOR Overview
 
  This implements CBOR -- Concise Binary Object Representation as
  defined in [RFC 7049] (https://tools.ietf.org/html/rfc7049). More
- info is at http://cbor.io.  This is a near-complete implementation of
- the specification. Limitations are listed further down.
+ information is at http://cbor.io.  This is a near-complete implementation of
+ the specification. [RFC 8742] (https://tools.ietf.org/html/rfc8742) CBOR
+ Sequences is also supported. Limitations are listed further down.
+
+ See @ref Encoding for general discussion on encoding,
+ @ref BasicDecode for general discussion on the basic decode features
+ and @ref SpiffyDecode for general discussion on the easier-to-use
+ decoder functions.
 
  CBOR is intentionally designed to be translatable to JSON, but not
  all CBOR can convert to JSON. See RFC 7049 for more info on how to
@@ -101,10 +109,9 @@
 
  - "Key": See "Label" above.
 
- - "Tag": Optional integer that can be added before each data item
- usually to indicate it is new or more specific data type. For
- example, a tag can indicate an integer is a date, or that a map is to
- be considered a type (analogous to a typedef in C).
+ - "Tag": A data item that is an explicitly labeled new data
+ type made up of the tagging integer and the tag content.
+ See @ref Tags-Overview and @ref Tag-Usage.
 
  - "Initial Byte": The first byte of an encoded item. Encoding and
  decoding of this byte is taken care of by the implementation.
@@ -154,6 +161,10 @@
  map. It is possible to use this API to construct and parse such
  labels, but it is not explicitly supported.
 
+ @anchor Encoding
+
+ ## Encoding
+
  A common encoding usage mode is to invoke the encoding twice. First
  with no output buffer to compute the length of the needed output
  buffer. Then the correct sized output buffer is allocated. Last the
@@ -214,14 +225,18 @@
  Many CBOR-based protocols start with an array or map. This makes them
  self-delimiting. No external length or end marker is needed to know
  the end. It is also possible not start this way, in which case this
- it is usually called a CBOR sequence which is described in [RFC 8742] (https://tools.ietf.org/html/rfc8742 ).
- This encoder supports either just by whether the first item added is an
- array, map or other.
+ it is usually called a CBOR sequence which is described in
+ [RFC 8742] (https://tools.ietf.org/html/rfc8742). This encoder supports
+ either just by whether the first item added is an array, map or other.
 
  @anchor Tags-Overview
- Any CBOR data item can be tagged to add semantics, define a new data
- type or such. Some tags are fully standardized and some are just
- registered. Others are not registered and used in a proprietary way.
+
+ ## Tags Overview
+
+ Any CBOR data item can be made into a tag to add semantics, define a
+ new data type or such. Some tags are fully standardized and some are
+ just registered. Others are not registered and used in a proprietary
+ way.
 
  Encoding and decoding of many of the registered tags is fully
  implemented by QCBOR. It is also possible to encode and decode tags
@@ -245,6 +260,8 @@
  that the new value doesn't collide with one that is registered. The
  value of these tags must be 256 or larger.
 
+ See also @ref CBORTags and @ref Tag-Usage
+
  The encoding side of tags not built-in is handled by
  QCBOREncode_AddTag() and is relatively simple. Tag decoding is more
  complex and mainly handled by QCBORDecode_GetNext(). Decoding of the
@@ -252,6 +269,9 @@
  implemented by the caller.
 
  @anchor Floating-Point
+
+ ## Floating-Point
+
  By default QCBOR fully supports IEEE 754 floating-point:
   - Encode/decode of double, single and half-precision
   - CBOR preferred serialization of floating-point
@@ -265,26 +285,25 @@
  representation of the double or float that preserves precision. Zero,
  NaN and infinity are always output as a half-precision, each taking
  just 2 bytes. This reduces the number of bytes needed to encode
- doubles and floats, especially if the zero, NaN and infinity are
+ double and single-precision, especially if zero, NaN and infinity are
  frequently used.
 
- To avoid use of preferred serialization when encoding, use
- QCBOREncode_AddDoubleNoPreferred() or
+ To avoid use of preferred serialization in the standard configuration
+ when encoding, use QCBOREncode_AddDoubleNoPreferred() or
  QCBOREncode_AddFloatNoPreferred().
 
  This implementation of preferred floating-point serialization and
  half-precision does not depend on the CPU having floating-point HW or
  the compiler bringing in a (sometimes large) library to compensate
- for lack of CPU support. The implementation uses shifts and masks
- rather than floating-point functions. It does however add object
- code.
+ for lack of CPU support. This implementation uses shifts and masks
+ rather than floating-point functions.
 
- To reduce object code, define QCBOR_DISABLE_PREFERRED_FLOAT. This
- will eliminate all support for preferred serialization and
- half-precision. An error will be returned when attempting to decode
- half-precision. A float will always be encoded and decoded as 32-bits
- and a double will always be encoded and decoded as 64 bits. This
- will reduce object code by about 900 bytes.
+ To reduce overall object code by about 900 bytes, define
+ QCBOR_DISABLE_PREFERRED_FLOAT. This will eliminate all support for
+ preferred serialization and half-precision. An error will be returned
+ when attempting to decode half-precision. A float will always be
+ encoded and decoded as 32-bits and a double will always be encoded
+ and decoded as 64 bits.
 
  Note that even if QCBOR_DISABLE_PREFERRED_FLOAT is not defined all
  the float-point encoding object code can be avoided by never calling
@@ -294,23 +313,26 @@
  On CPUs that have no floating-point hardware,
  QCBOR_DISABLE_FLOAT_HW_USE should be defined in most cases. If it is
  not, then the compiler will bring in possibly large software
- libraries to compensate or QCBOR will not compile. Defining
- QCBOR_DISABLE_FLOAT_HW_USE reduces object code size on CPUs with
- floating-point hardware by a tiny amount.
+ libraries to compensate. Defining QCBOR_DISABLE_FLOAT_HW_USE reduces
+ object code size on CPUs with floating-point hardware by a tiny
+ amount and eliminates the need for <math.h>
 
- If QCBOR_DISABLE_FLOAT_HW_USE is defined and
- QCBOR_DISABLE_PREFERRED_FLOAT is not defined, then the only
- functionality lost is the decoding of floating-point dates.  An error
- will be returned if they are encountered.
+ When QCBOR_DISABLE_FLOAT_HW_USE is defined, trying to decoding
+ floating-point dates will give error
+ @ref QCBOR_ERR_FLOAT_DATE_DISABLED and decoded single-precision
+ numbers will be returned as @ref QCBOR_TYPE_FLOAT instead of
+ converting them to double as usual.
 
  If both QCBOR_DISABLE_FLOAT_HW_USE and QCBOR_DISABLE_PREFERRED_FLOAT
- are defined, then the only thing QCBOR can do is encode/decode a
- float as 32-bits and a double as 64-bits. Floating-point epoch dates
- will be unsupported.
+ are defined, then the only thing QCBOR can do is encode/decode a C
+ float type as 32-bits and a C double type as 64-bits. Floating-point
+ epoch dates will be unsupported.
+
+ ## Limitations
 
  Summary Limits of this implementation:
  - The entire encoded CBOR must fit into contiguous memory.
- - Max size of encoded / decoded CBOR data is @c UINT32_MAX (4GB).
+ - Max size of encoded / decoded CBOR data is a few bytes less than @c UINT32_MAX (4GB).
  - Max array / map nesting level when encoding / decoding is
    @ref QCBOR_MAX_ARRAY_NESTING (this is typically 15).
  - Max items in an array or map when encoding / decoding is
@@ -320,7 +342,7 @@
  - Epoch dates limited to @c INT64_MAX (+/- 292 billion years).
  - Exponents for bigfloats and decimal integers are limited to @c INT64_MAX.
  - Tags on labels are ignored during decoding.
- - There is no duplicate detection of map labels (but duplicates are passed on).
+ - The maximum tag nesting is @c QCBOR_MAX_TAGS_PER_ITEM (typically 4).
  - Works only on 32- and 64-bit CPUs (modifications could make it work
    on 16-bit CPUs).
 
@@ -333,19 +355,30 @@
  <stdint.h> also requires this. It is possible to modify this
  implementation for another integer representation, but all modern
  machines seem to be two's compliment.
-
  */
 
 
-/*
+/**
  The size of the buffer to be passed to QCBOREncode_EncodeHead(). It is one
  byte larger than sizeof(uint64_t) + 1, the actual maximum size of the
- head of a CBOR data item. because QCBOREncode_EncodeHead() needs
+ head of a CBOR data item because QCBOREncode_EncodeHead() needs
  one extra byte to work.
  */
 #define QCBOR_HEAD_BUFFER_SIZE  (sizeof(uint64_t) + 2)
 
 
+/**
+ Output the full CBOR tag. See @ref CBORTags, @ref Tag-Usage and
+ @ref Tags-Overview.
+ */
+#define QCBOR_ENCODE_AS_TAG      0
+
+/**
+ Output only the 'borrowed' content format for the relevant tag.
+ See @ref CBORTags, @ref Tag-Usage and @ref Tags-Overview.
+ */
+#define QCBOR_ENCODE_AS_BORROWED 1
+
 
 /**
  QCBOREncodeContext is the data type that holds context for all the
@@ -475,6 +508,23 @@
  lengths greater. This limit to 4GB for a text string should not be a
  problem.
 
+ Text lines in Internet protocols (on the wire) are delimited by
+ either a CRLF or just an LF. Officially many protocols specify CRLF,
+ but implementations often work with either. CBOR type 3 text can be
+ either line ending, even a mixture of both.
+
+ Operating systems usually have a line end convention. Windows uses
+ CRLF. Linux and MacOS use LF. Some applications on a given OS may
+ work with either and some may not.
+
+ The majority of use cases and CBOR protocols using type 3 text will
+ work with either line ending. However, some use cases or protocols
+ may not work with either in which case translation to and/or from the
+ local line end convention, typically that of the OS, is necessary.
+
+ QCBOR does no line ending translation for type 3 text when encoding
+ and decoding.
+
  Error handling is the same as QCBOREncode_AddInt64().
  */
 static void QCBOREncode_AddText(QCBOREncodeContext *pCtx, UsefulBufC Text);
@@ -603,7 +653,6 @@
 static void QCBOREncode_AddFloatNoPreferredToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, float fNum);
 
 
-
 /**
  @brief Add an optional tag.
 
@@ -627,14 +676,15 @@
  tags. See QCBORDecode_GetNext() for discussion of decoding custom
  tags.
 */
-void QCBOREncode_AddTag(QCBOREncodeContext *pCtx,uint64_t uTag);
+void QCBOREncode_AddTag(QCBOREncodeContext *pCtx, uint64_t uTag);
 
 
 /**
  @brief  Add an epoch-based date.
 
  @param[in] pCtx  The encoding context to add the date to.
- @param[in] date  Number of seconds since 1970-01-01T00:00Z in UTC time.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
+ @param[in] nDate  Number of seconds since 1970-01-01T00:00Z in UTC time.
 
  As per RFC 7049 this is similar to UNIX/Linux/POSIX dates. This is
  the most compact way to specify a date and time in CBOR. Note that
@@ -659,11 +709,31 @@
 
  Error handling is the same as QCBOREncode_AddInt64().
  */
-static void QCBOREncode_AddDateEpoch(QCBOREncodeContext *pCtx, int64_t date);
+static void QCBOREncode_AddTDateEpoch(QCBOREncodeContext *pCtx,
+                                      uint8_t             uTagRequirement,
+                                      int64_t             nDate);
 
-static void QCBOREncode_AddDateEpochToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t date);
+static void QCBOREncode_AddTDateEpochToMapSZ(QCBOREncodeContext *pCtx,
+                                             const char         *szLabel,
+                                             uint8_t             uTagRequirement,
+                                             int64_t             nDate);
 
-static  void QCBOREncode_AddDateEpochToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t date);
+static void QCBOREncode_AddTDateEpochToMapN(QCBOREncodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            uint8_t             uTagRequirement,
+                                            int64_t             nDate);
+
+
+static void QCBOREncode_AddDateEpoch(QCBOREncodeContext *pCtx,
+                                     int64_t             nDate);
+
+static void QCBOREncode_AddDateEpochToMap(QCBOREncodeContext *pCtx,
+                                          const char         *szLabel,
+                                          int64_t             nDate);
+
+static void QCBOREncode_AddDateEpochToMapN(QCBOREncodeContext *pCtx,
+                                           int64_t             nLabel,
+                                           int64_t             nDate);
 
 
 /**
@@ -686,11 +756,11 @@
 static void QCBOREncode_AddBytesToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
 
 
-
 /**
  @brief Add a binary UUID to the encoded output.
 
  @param[in] pCtx   The encoding context to add the UUID to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] Bytes  Pointer and length of the binary UUID.
 
  A binary UUID as defined in [RFC 4122]
@@ -699,6 +769,21 @@
  It is output as CBOR major type 2, a binary string, with tag @ref
  CBOR_TAG_BIN_UUID indicating the binary string is a UUID.
  */
+static void QCBOREncode_AddTBinaryUUID(QCBOREncodeContext *pCtx,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBufC          Bytes);
+
+static void QCBOREncode_AddTBinaryUUIDToMapSZ(QCBOREncodeContext *pCtx,
+                                              const char         *szLabel,
+                                              uint8_t             uTagRequirement,
+                                              UsefulBufC          Bytes);
+
+static void QCBOREncode_AddTBinaryUUIDToMapN(QCBOREncodeContext *pCtx,
+                                             int64_t             nLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBufC          Bytes);
+
+
 static void QCBOREncode_AddBinaryUUID(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
 
 static void QCBOREncode_AddBinaryUUIDToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
@@ -710,6 +795,7 @@
  @brief Add a positive big number to the encoded output.
 
  @param[in] pCtx   The encoding context to add the big number to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] Bytes  Pointer and length of the big number.
 
  Big numbers are integers larger than 64-bits. Their format is
@@ -723,17 +809,38 @@
  COSE which defines representations for keys chose not to use this
  particular type.
  */
-static void QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
+static void QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pCtx,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC          Bytes);
 
-static void QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
+static void QCBOREncode_AddTPositiveBignumToMapSZ(QCBOREncodeContext *pCtx,
+                                                  const char         *szLabel,
+                                                  uint8_t             uTagRequirement,
+                                                  UsefulBufC          Bytes);
 
-static void QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
+static void QCBOREncode_AddTPositiveBignumToMapN(QCBOREncodeContext *pCtx,
+                                                 int64_t             nLabel,
+                                                 uint8_t             uTagRequirement,
+                                                 UsefulBufC          Bytes);
+
+
+static void QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx,
+                                          UsefulBufC          Bytes);
+
+static void QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx,
+                                               const char         *szLabel,
+                                               UsefulBufC          Bytes);
+
+static void QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx,
+                                                int64_t             nLabel,
+                                                UsefulBufC          Bytes);
 
 
 /**
  @brief Add a negative big number to the encoded output.
 
  @param[in] pCtx   The encoding context to add the big number to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] Bytes  Pointer and length of the big number.
 
  Big numbers are integers larger than 64-bits. Their format is
@@ -747,11 +854,31 @@
  COSE which defines representations for keys chose not to use this
  particular type.
  */
-static void QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
+static void QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pCtx,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC          Bytes);
 
-static void QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes);
+static void QCBOREncode_AddTNegativeBignumToMapSZ(QCBOREncodeContext *pCtx,
+                                                  const char         *szLabel,
+                                                  uint8_t             uTagRequirement,
+                                                  UsefulBufC          Bytes);
 
-static void QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes);
+static void QCBOREncode_AddTNegativeBignumToMapN(QCBOREncodeContext *pCtx,
+                                                 int64_t             nLabel,
+                                                 uint8_t             uTagRequirement,
+                                                 UsefulBufC          Bytes);
+
+
+static void QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx,
+                                          UsefulBufC          Bytes);
+
+static void QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx,
+                                               const char         *szLabel,
+                                               UsefulBufC          Bytes);
+
+static void QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx,
+                                                int64_t             nLabel,
+                                                UsefulBufC          Bytes);
 
 
 #ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
@@ -759,6 +886,7 @@
  @brief Add a decimal fraction to the encoded output.
 
  @param[in] pCtx            The encoding context to add the decimal fraction to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] nMantissa       The mantissa.
  @param[in] nBase10Exponent The exponent.
 
@@ -791,6 +919,24 @@
 
  See @ref expAndMantissa for decoded representation.
  */
+static void QCBOREncode_AddTDecimalFraction(QCBOREncodeContext *pCtx,
+                                            uint8_t             uTagRequirement,
+                                            int64_t             nMantissa,
+                                            int64_t             nBase10Exponent);
+
+static void QCBOREncode_AddTDecimalFractionToMapSZ(QCBOREncodeContext *pCtx,
+                                                   const char         *szLabel,
+                                                   uint8_t             uTagRequirement,
+                                                   int64_t             nMantissa,
+                                                   int64_t             nBase10Exponent);
+
+static void QCBOREncode_AddTDecimalFractionToMapN(QCBOREncodeContext *pCtx,
+                                                  int64_t             nLabel,
+                                                  uint8_t             uTagRequirement,
+                                                  int64_t             nMantissa,
+                                                  int64_t             nBase10Exponent);
+
+
 static void QCBOREncode_AddDecimalFraction(QCBOREncodeContext *pCtx,
                                            int64_t             nMantissa,
                                            int64_t             nBase10Exponent);
@@ -804,11 +950,11 @@
                                                  int64_t             nLabel,
                                                  int64_t             nMantissa,
                                                  int64_t             nBase10Exponent);
-
 /**
  @brief Add a decimal fraction with a big number mantissa to the encoded output.
 
  @param[in] pCtx            The encoding context to add the decimal fraction to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] Mantissa        The mantissa.
  @param[in] bIsNegative     false if mantissa is positive, true if negative.
  @param[in] nBase10Exponent The exponent.
@@ -819,16 +965,37 @@
 
  See @ref expAndMantissa for decoded representation.
  */
+static void QCBOREncode_AddTDecimalFractionBigNum(QCBOREncodeContext *pCtx,
+                                                  uint8_t             uTagRequirement,
+                                                  UsefulBufC          Mantissa,
+                                                  bool                bIsNegative,
+                                                  int64_t             nBase10Exponent);
+
+static void QCBOREncode_AddTDecimalFractionBigNumToMapSZ(QCBOREncodeContext *pCtx,
+                                                         const char         *szLabel,
+                                                         uint8_t             uTagRequirement,
+                                                         UsefulBufC          Mantissa,
+                                                         bool                bIsNegative,
+                                                         int64_t             nBase10Exponent);
+
+static void QCBOREncode_AddTDecimalFractionBigNumToMapN(QCBOREncodeContext *pCtx,
+                                                        int64_t             nLabel,
+                                                        uint8_t             uTagRequirement,
+                                                        UsefulBufC          Mantissa,
+                                                        bool                bIsNegative,
+                                                        int64_t             nBase10Exponent);
+
+
 static void QCBOREncode_AddDecimalFractionBigNum(QCBOREncodeContext *pCtx,
                                                  UsefulBufC          Mantissa,
                                                  bool                bIsNegative,
                                                  int64_t             nBase10Exponent);
 
-static void QCBOREncode_AddDecimalFractionBigNumToMap(QCBOREncodeContext *pCtx,
-                                                      const char         *szLabel,
-                                                      UsefulBufC          Mantissa,
-                                                      bool                bIsNegative,
-                                                      int64_t             nBase10Exponent);
+static void QCBOREncode_AddDecimalFractionBigNumToMapSZ(QCBOREncodeContext *pCtx,
+                                                        const char         *szLabel,
+                                                        UsefulBufC          Mantissa,
+                                                        bool                bIsNegative,
+                                                        int64_t             nBase10Exponent);
 
 static void QCBOREncode_AddDecimalFractionBigNumToMapN(QCBOREncodeContext *pCtx,
                                                        int64_t             nLabel,
@@ -840,6 +1007,7 @@
  @brief Add a big floating-point number to the encoded output.
 
  @param[in] pCtx            The encoding context to add the bigfloat to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] nMantissa       The mantissa.
  @param[in] nBase2Exponent  The exponent.
 
@@ -870,6 +1038,24 @@
 
  See @ref expAndMantissa for decoded representation.
  */
+static void QCBOREncode_AddTBigFloat(QCBOREncodeContext *pCtx,
+                                     uint8_t             uTagRequirement,
+                                     int64_t             nMantissa,
+                                     int64_t             nBase2Exponent);
+
+static void QCBOREncode_AddTBigFloatToMapSZ(QCBOREncodeContext *pCtx,
+                                            const char         *szLabel,
+                                            uint8_t             uTagRequirement,
+                                            int64_t             nMantissa,
+                                            int64_t             nBase2Exponent);
+
+static void QCBOREncode_AddTBigFloatToMapN(QCBOREncodeContext *pCtx,
+                                           int64_t             nLabel,
+                                           uint8_t             uTagRequirement,
+                                           int64_t             nMantissa,
+                                           int64_t             nBase2Exponent);
+
+
 static void QCBOREncode_AddBigFloat(QCBOREncodeContext *pCtx,
                                     int64_t             nMantissa,
                                     int64_t             nBase2Exponent);
@@ -884,12 +1070,12 @@
                                           int64_t             nMantissa,
                                           int64_t             nBase2Exponent);
 
-
 /**
  @brief Add a big floating-point number with a big number mantissa to
         the encoded output.
 
  @param[in] pCtx            The encoding context to add the bigfloat to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] Mantissa        The mantissa.
  @param[in] bIsNegative     false if mantissa is positive, true if negative.
  @param[in] nBase2Exponent  The exponent.
@@ -900,6 +1086,27 @@
 
  See @ref expAndMantissa for decoded representation.
  */
+static void QCBOREncode_AddTBigFloatBigNum(QCBOREncodeContext *pCtx,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC          Mantissa,
+                                           bool                bIsNegative,
+                                           int64_t             nBase2Exponent);
+
+static void QCBOREncode_AddTBigFloatBigNumToMapSZ(QCBOREncodeContext *pCtx,
+                                                  const char         *szLabel,
+                                                  uint8_t             uTagRequirement,
+                                                  UsefulBufC          Mantissa,
+                                                  bool                bIsNegative,
+                                                  int64_t             nBase2Exponent);
+
+static void QCBOREncode_AddTBigFloatBigNumToMapN(QCBOREncodeContext *pCtx,
+                                                 int64_t             nLabel,
+                                                 uint8_t             uTagRequirement,
+                                                 UsefulBufC          Mantissa,
+                                                 bool                bIsNegative,
+                                                 int64_t             nBase2Exponent);
+
+
 static void QCBOREncode_AddBigFloatBigNum(QCBOREncodeContext *pCtx,
                                           UsefulBufC          Mantissa,
                                           bool                bIsNegative,
@@ -923,6 +1130,7 @@
  @brief Add a text URI to the encoded output.
 
  @param[in] pCtx  The encoding context to add the URI to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] URI   Pointer and length of the URI.
 
  The format of URI must be per [RFC 3986]
@@ -936,17 +1144,38 @@
 
       QCBOREncode_AddURI(pCtx, UsefulBuf_FromSZ(szURI));
  */
-static void QCBOREncode_AddURI(QCBOREncodeContext *pCtx, UsefulBufC URI);
+static void QCBOREncode_AddTURI(QCBOREncodeContext *pCtx,
+                                uint8_t             uTagRequirement,
+                                UsefulBufC          URI);
 
-static void QCBOREncode_AddURIToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC URI);
+static void QCBOREncode_AddTURIToMapSZ(QCBOREncodeContext *pCtx,
+                                       const char         *szLabel,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBufC          URI);
 
-static void QCBOREncode_AddURIToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC URI);
+static void QCBOREncode_AddTURIToMapN(QCBOREncodeContext *pCtx,
+                                      int64_t             nLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC          URI);
+
+
+static void QCBOREncode_AddURI(QCBOREncodeContext *pCtx,
+                               UsefulBufC          URI);
+
+static void QCBOREncode_AddURIToMap(QCBOREncodeContext *pCtx,
+                                    const char         *szLabel,
+                                    UsefulBufC          URI);
+
+static void QCBOREncode_AddURIToMapN(QCBOREncodeContext *pCtx,
+                                     int64_t             nLabel,
+                                     UsefulBufC          URI);
 
 
 /**
  @brief Add Base64-encoded text to encoded output.
 
  @param[in] pCtx     The encoding context to add the base-64 text to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] B64Text  Pointer and length of the base-64 encoded text.
 
  The text content is Base64 encoded data per [RFC 4648]
@@ -955,17 +1184,39 @@
  It is output as CBOR major type 3, a text string, with tag @ref
  CBOR_TAG_B64 indicating the text string is Base64 encoded.
  */
-static void QCBOREncode_AddB64Text(QCBOREncodeContext *pCtx, UsefulBufC B64Text);
+static void QCBOREncode_AddTB64Text(QCBOREncodeContext *pCtx,
+                                    uint8_t             uTagRequirement,
+                                    UsefulBufC          B64Text);
 
-static void QCBOREncode_AddB64TextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text);
+static void QCBOREncode_AddTB64TextToMapSZ(QCBOREncodeContext *pCtx,
+                                           const char         *szLabel,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC          B64Text);
 
-static void QCBOREncode_AddB64TextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text);
+static void QCBOREncode_AddTB64TextToMapN(QCBOREncodeContext *pCtx,
+                                          int64_t nLabel,
+                                          uint8_t uTagRequirement,
+                                          UsefulBufC B64Text);
+
+
+static void QCBOREncode_AddB64Text(QCBOREncodeContext *pCtx,
+                                   UsefulBufC          B64Text);
+
+static void QCBOREncode_AddB64TextToMap(QCBOREncodeContext *pCtx,
+                                        const char         *szLabel,
+                                        UsefulBufC          B64Text);
+
+static void QCBOREncode_AddB64TextToMapN(QCBOREncodeContext *pCtx,
+                                         int64_t             nLabel,
+                                         UsefulBufC          B64Text);
+
 
 
 /**
  @brief Add base64url encoded data to encoded output.
 
  @param[in] pCtx     The encoding context to add the base64url to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] B64Text  Pointer and length of the base64url encoded text.
 
  The text content is base64URL encoded text as per [RFC 4648]
@@ -974,17 +1225,38 @@
  It is output as CBOR major type 3, a text string, with tag @ref
  CBOR_TAG_B64URL indicating the text string is a Base64url encoded.
  */
-static void QCBOREncode_AddB64URLText(QCBOREncodeContext *pCtx, UsefulBufC B64Text);
+static void QCBOREncode_AddTB64URLText(QCBOREncodeContext *pCtx,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBufC          B64Text);
 
-static void QCBOREncode_AddB64URLTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text);
+static void QCBOREncode_AddTB64URLTextToMapSZ(QCBOREncodeContext *pCtx,
+                                              const char         *szLabel,
+                                              uint8_t             uTagRequirement,
+                                              UsefulBufC          B64Text);
 
-static void QCBOREncode_AddB64URLTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text);
+static void QCBOREncode_AddTB64URLTextToMapN(QCBOREncodeContext *pCtx,
+                                             int64_t             nLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBufC          B64Text);
+
+
+static void QCBOREncode_AddB64URLText(QCBOREncodeContext *pCtx,
+                                      UsefulBufC          B64Text);
+
+static void QCBOREncode_AddB64URLTextToMap(QCBOREncodeContext *pCtx,
+                                           const char         *szLabel,
+                                           UsefulBufC          B64Text);
+
+static void QCBOREncode_AddB64URLTextToMapN(QCBOREncodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            UsefulBufC          B64Text);
 
 
 /**
  @brief Add Perl Compatible Regular Expression.
 
  @param[in] pCtx    The encoding context to add the regular expression to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] Regex   Pointer and length of the regular expression.
 
  The text content is Perl Compatible Regular
@@ -993,38 +1265,92 @@
  It is output as CBOR major type 3, a text string, with tag @ref
  CBOR_TAG_REGEX indicating the text string is a regular expression.
  */
-static void QCBOREncode_AddRegex(QCBOREncodeContext *pCtx, UsefulBufC Regex);
+static void QCBOREncode_AddTRegex(QCBOREncodeContext *pCtx,
+                                  uint8_t            uTagRequirement,
+                                  UsefulBufC         Regex);
 
-static void QCBOREncode_AddRegexToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Regex);
+static void QCBOREncode_AddTRegexToMapSZ(QCBOREncodeContext *pCtx,
+                                         const char         *szLabel,
+                                         uint8_t             uTagRequirement,
+                                         UsefulBufC          Regex);
 
-static void QCBOREncode_AddRegexToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Regex);
+static void QCBOREncode_AddTRegexToMapN(QCBOREncodeContext *pCtx,
+                                        int64_t             nLabel,
+                                        uint8_t             uTagRequirement,
+                                        UsefulBufC          Regex);
+
+
+static void QCBOREncode_AddRegex(QCBOREncodeContext *pCtx,
+                                 UsefulBufC          Regex);
+
+static void QCBOREncode_AddRegexToMap(QCBOREncodeContext *pCtx,
+                                      const char         *szLabel,
+                                      UsefulBufC          Regex);
+
+static void QCBOREncode_AddRegexToMapN(QCBOREncodeContext *pCtx,
+                                       int64_t             nLabel,
+                                       UsefulBufC          Regex);
 
 
 /**
- @brief MIME encoded text to the encoded output.
+ @brief MIME encoded data to the encoded output.
 
- @param[in] pCtx      The encoding context to add the MIME data to.
- @param[in] MIMEData  Pointer and length of the regular expression.
+ @param[in] pCtx             The encoding context to add the MIME data to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
+                             @ref QCBOR_ENCODE_AS_BORROWED.
+ @param[in] MIMEData         Pointer and length of the MIME data.
 
  The text content is in MIME format per [RFC 2045]
- (https://tools.ietf.org/html/rfc2045) including the headers. Note
- that this only supports text-format MIME. Binary MIME is not
- supported.
+ (https://tools.ietf.org/html/rfc2045) including the headers.
 
- It is output as CBOR major type 3, a text string, with tag
- @ref CBOR_TAG_MIME indicating the text string is MIME data.
+ It is output as CBOR major type 2, a binary string, with tag @ref
+ CBOR_TAG_BINARY_MIME indicating the string is MIME data.  This
+ outputs tag 257, not tag 36, as it can carry any type of MIME binary,
+ 7-bit, 8-bit, quoted-printable and base64 where tag 36 cannot.
+
+ Previous versions of QCBOR, those before spiffy decode, output tag
+ 36. Decoding supports both tag 36 and 257.  (if the old behavior with
+ tag 36 is needed, copy the inline functions below and change the tag
+ number).
+
+ See also QCBORDecode_GetMIMEMessage() and
+ @ref QCBOR_TYPE_BINARY_MIME.
+
+ This does no translation of line endings. See QCBOREncode_AddText()
+ for a discussion of line endings in CBOR.
  */
-static void QCBOREncode_AddMIMEData(QCBOREncodeContext *pCtx, UsefulBufC MIMEData);
+static void QCBOREncode_AddTMIMEData(QCBOREncodeContext *pCtx,
+                                     uint8_t             uTagRequirement,
+                                     UsefulBufC          MIMEData);
 
-static void QCBOREncode_AddMIMEDataToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC MIMEData);
+static void QCBOREncode_AddTMIMEDataToMapSZ(QCBOREncodeContext *pCtx,
+                                            const char         *szLabel,
+                                            uint8_t             uTagRequirement,
+                                            UsefulBufC          MIMEData);
 
-static void QCBOREncode_AddMIMEDataToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC MIMEData);
+static void QCBOREncode_AddTMIMEDataToMapN(QCBOREncodeContext *pCtx,
+                                           int64_t             nLabel,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC          MIMEData);
+
+
+static void QCBOREncode_AddMIMEData(QCBOREncodeContext *pCtx,
+                                    UsefulBufC          MIMEData);
+
+static void QCBOREncode_AddMIMEDataToMap(QCBOREncodeContext *pCtx,
+                                         const char         *szLabel,
+                                         UsefulBufC          MIMEData);
+
+static void QCBOREncode_AddMIMEDataToMapN(QCBOREncodeContext *pCtx,
+                                          int64_t             nLabel,
+                                          UsefulBufC          MIMEData);
 
 
 /**
  @brief  Add an RFC 3339 date string
 
  @param[in] pCtx    The encoding context to add the date to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or @ref QCBOR_ENCODE_AS_BORROWED.
  @param[in] szDate  Null-terminated string with date to add.
 
  The string szDate should be in the form of [RFC 3339]
@@ -1039,13 +1365,32 @@
 
  Error handling is the same as QCBOREncode_AddInt64().
  */
-static void QCBOREncode_AddDateString(QCBOREncodeContext *pCtx, const char *szDate);
+static void QCBOREncode_AddTDateString(QCBOREncodeContext *pCtx,
+                                       uint8_t             uTagRequirement,
+                                       const char         *szDate);
 
-static void QCBOREncode_AddDateStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szDate);
+static void QCBOREncode_AddTDateStringToMapSZ(QCBOREncodeContext *pCtx,
+                                              const char         *szLabel,
+                                              uint8_t             uTagRequirement,
+                                              const char         *szDate);
 
-static void QCBOREncode_AddDateStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szDate);
+static void QCBOREncode_AddTDateStringToMapN(QCBOREncodeContext *pCtx,
+                                             int64_t             nLabel,
+                                             uint8_t             uTagRequirement,
+                                             const char         *szDate);
 
 
+static void QCBOREncode_AddDateString(QCBOREncodeContext *pCtx,
+                                      const char         *szDate);
+
+static void QCBOREncode_AddDateStringToMap(QCBOREncodeContext *pCtx,
+                                           const char         *szLabel,
+                                           const char         *szDate);
+
+static void QCBOREncode_AddDateStringToMapN(QCBOREncodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            const char         *szDate);
+
 /**
  @brief  Add a standard Boolean.
 
@@ -1203,7 +1548,6 @@
 static void QCBOREncode_OpenMapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel);
 
 
-
 /**
  @brief Close an open map.
 
@@ -1421,12 +1765,13 @@
 */
 static int QCBOREncode_IsBufferNULL(QCBOREncodeContext *pCtx);
 
- /**
+
+/**
  @brief Get the encoding error state.
 
  @param[in] pCtx  The encoding context.
 
- @return One of \ref QCBORError. See return values from
+ @return One of @ref QCBORError. See return values from
          QCBOREncode_Finish()
 
  Normally encoding errors need only be handled at the end of encoding
@@ -1446,14 +1791,14 @@
  @param uMinLen      The minimum number of bytes to encode uNumber. Almost always
                      this is 0 to use preferred minimal encoding. If this is 4,
                      then even the values 0xffff and smaller will be encoded
-                     as in 4 bytes. This is used primarily when encoding a
+                     in 4 bytes. This is used primarily when encoding a
                      float or double put into uNumber as the leading zero bytes
                      for them must be encoded.
  @param uNumber      The numeric argument part of the CBOR head.
  @return             Pointer and length of the encoded head or
-                     @NULLUsefulBufC if the output buffer is too small.
+                     @ref NULLUsefulBufC if the output buffer is too small.
 
- Callers to need to call this for normal CBOR encoding. Note that it doesn't even
+ Callers do not to need to call this for normal CBOR encoding. Note that it doesn't even
  take a @ref QCBOREncodeContext argument.
 
  This encodes the major type and argument part of a data item. The
@@ -1480,10 +1825,9 @@
 
 
 
-/* ===========================================================================
- BEGINNING OF PRIVATE INLINE IMPLEMENTATION
-
- =========================================================================== */
+/* =========================================================================
+     BEGINNING OF PRIVATE INLINE IMPLEMENTATION
+   ========================================================================= */
 
 /**
  @brief Semi-private method to add a buffer full of bytes to encoded output
@@ -1579,15 +1923,26 @@
  @brief  Semi-private method to add bigfloats and decimal fractions.
 
  @param[in] pCtx             The encoding context to add the value to.
- @param[in] uTag             The type 6 tag indicating what this is to be
+ @param[in] uTag             The type 6 tag indicating what this is to be.
  @param[in] BigNumMantissa   Is @ref NULLUsefulBufC if mantissa is an
                              @c int64_t or the actual big number mantissa
                              if not.
  @param[in] nMantissa        The @c int64_t mantissa if it is not a big number.
  @param[in] nExponent        The exponent.
 
- This adds a tagged array with two members, the mantissa and exponent. The
- mantissa can be either a big number or an @c int64_t.
+ This outputs either the @ref CBOR_TAG_DECIMAL_FRACTION or @ref
+ CBOR_TAG_BIGFLOAT tag. if @c uTag is @ref CBOR_TAG_INVALID64, then
+ this outputs the "borrowed" content format.
+
+ The tag content output by this is an array with two members, the
+ exponent and then the mantissa. The mantissa can be either a big
+ number or an @c int64_t.
+
+ This implementation cannot output an exponent further from 0 than
+ @c INT64_MAX.
+
+ To output a mantissa that is bewteen INT64_MAX and UINT64_MAX from 0,
+ it must be as a big number.
 
  Typically, QCBOREncode_AddDecimalFraction(), QCBOREncode_AddBigFloat(),
  QCBOREncode_AddDecimalFractionBigNum() or QCBOREncode_AddBigFloatBigNum()
@@ -1632,719 +1987,1195 @@
 
 
 
-static inline void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t uNum)
+static inline void
+QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, int64_t uNum)
 {
    // Use _AddBuffer() because _AddSZString() is defined below, not above
-   QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel));
-   QCBOREncode_AddInt64(pCtx, uNum);
+   QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel));
+   QCBOREncode_AddInt64(pMe, uNum);
 }
 
-static inline void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t uNum)
+static inline void
+QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, int64_t uNum)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddInt64(pCtx, uNum);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddInt64(pMe, uNum);
 }
 
 
-static inline void QCBOREncode_AddUInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, uint64_t uNum)
+static inline void
+QCBOREncode_AddUInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, uint64_t uNum)
 {
    // Use _AddBuffer() because _AddSZString() is defined below, not above
-   QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel));
-   QCBOREncode_AddUInt64(pCtx, uNum);
+   QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel));
+   QCBOREncode_AddUInt64(pMe, uNum);
 }
 
-static inline void QCBOREncode_AddUInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, uint64_t uNum)
+static inline void
+QCBOREncode_AddUInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint64_t uNum)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddUInt64(pCtx, uNum);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddUInt64(pMe, uNum);
 }
 
 
-static inline void QCBOREncode_AddText(QCBOREncodeContext *pCtx, UsefulBufC Text)
+static inline void
+QCBOREncode_AddText(QCBOREncodeContext *pMe, UsefulBufC Text)
 {
-   QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_TEXT_STRING, Text);
+   QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_TYPE_TEXT_STRING, Text);
 }
 
-static inline void QCBOREncode_AddTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Text)
+static inline void
+QCBOREncode_AddTextToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Text)
 {
-   // Use _AddBuffer() because _AddSZString() is defined below, not above
-   QCBOREncode_AddText(pCtx, UsefulBuf_FromSZ(szLabel));
-   QCBOREncode_AddText(pCtx, Text);
+   QCBOREncode_AddText(pMe, UsefulBuf_FromSZ(szLabel));
+   QCBOREncode_AddText(pMe, Text);
 }
 
-static inline void QCBOREncode_AddTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Text)
+static inline void
+QCBOREncode_AddTextToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Text)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddText(pCtx, Text);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddText(pMe, Text);
 }
 
 
-inline static void QCBOREncode_AddSZString(QCBOREncodeContext *pCtx, const char *szString)
+inline static void
+QCBOREncode_AddSZString(QCBOREncodeContext *pMe, const char *szString)
 {
-   QCBOREncode_AddText(pCtx, UsefulBuf_FromSZ(szString));
+   QCBOREncode_AddText(pMe, UsefulBuf_FromSZ(szString));
 }
 
-static inline void QCBOREncode_AddSZStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szString)
+static inline void
+QCBOREncode_AddSZStringToMap(QCBOREncodeContext *pMe, const char *szLabel, const char *szString)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddSZString(pCtx, szString);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddSZString(pMe, szString);
 }
 
-static inline void QCBOREncode_AddSZStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szString)
+static inline void
+QCBOREncode_AddSZStringToMapN(QCBOREncodeContext *pMe, int64_t nLabel, const char *szString)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddSZString(pCtx, szString);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddSZString(pMe, szString);
 }
 
 
-static inline void QCBOREncode_AddDoubleToMap(QCBOREncodeContext *pCtx, const char *szLabel, double dNum)
+static inline void
+QCBOREncode_AddDoubleToMap(QCBOREncodeContext *pMe, const char *szLabel, double dNum)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddDouble(pCtx, dNum);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddDouble(pMe, dNum);
 }
 
-static inline void QCBOREncode_AddDoubleToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, double dNum)
+static inline void
+QCBOREncode_AddDoubleToMapN(QCBOREncodeContext *pMe, int64_t nLabel, double dNum)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddDouble(pCtx, dNum);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddDouble(pMe, dNum);
 }
 
-static inline void QCBOREncode_AddFloatToMap(QCBOREncodeContext *pCtx, const char *szLabel, float dNum)
+static inline void
+QCBOREncode_AddFloatToMap(QCBOREncodeContext *pMe, const char *szLabel, float dNum)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddFloat(pCtx, dNum);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddFloat(pMe, dNum);
 }
 
-static inline void QCBOREncode_AddFloatToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, float fNum)
+static inline void
+QCBOREncode_AddFloatToMapN(QCBOREncodeContext *pMe, int64_t nLabel, float fNum)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddFloat(pCtx, fNum);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddFloat(pMe, fNum);
 }
 
-static inline void QCBOREncode_AddDoubleNoPreferredToMap(QCBOREncodeContext *pCtx, const char *szLabel, double dNum)
+static inline void
+QCBOREncode_AddDoubleNoPreferredToMap(QCBOREncodeContext *pMe, const char *szLabel, double dNum)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddDoubleNoPreferred(pCtx, dNum);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddDoubleNoPreferred(pMe, dNum);
 }
 
-static inline void QCBOREncode_AddDoubleNoPreferredToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, double dNum)
+static inline void
+QCBOREncode_AddDoubleNoPreferredToMapN(QCBOREncodeContext *pMe, int64_t nLabel, double dNum)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddDoubleNoPreferred(pCtx, dNum);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddDoubleNoPreferred(pMe, dNum);
 }
 
-static inline void QCBOREncode_AddFloatNoPreferredToMap(QCBOREncodeContext *pCtx, const char *szLabel, float dNum)
+static inline void
+QCBOREncode_AddFloatNoPreferredToMap(QCBOREncodeContext *pMe, const char *szLabel, float dNum)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddFloatNoPreferred(pCtx, dNum);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddFloatNoPreferred(pMe, dNum);
 }
 
-static inline void QCBOREncode_AddFloatNoPreferredToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, float dNum)
+static inline void
+QCBOREncode_AddFloatNoPreferredToMapN(QCBOREncodeContext *pMe, int64_t nLabel, float dNum)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddFloatNoPreferred(pCtx, dNum);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddFloatNoPreferred(pMe, dNum);
 }
 
 
-static inline void QCBOREncode_AddDateEpoch(QCBOREncodeContext *pCtx, int64_t date)
+
+static inline void
+QCBOREncode_AddTDateEpoch(QCBOREncodeContext *pMe, uint8_t uTag, int64_t nDate)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_EPOCH);
-   QCBOREncode_AddInt64(pCtx, date);
+   if(uTag == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_DATE_EPOCH);
+   }
+   QCBOREncode_AddInt64(pMe, nDate);
 }
 
-static inline void QCBOREncode_AddDateEpochToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t date)
+static inline void
+QCBOREncode_AddTDateEpochToMapSZ(QCBOREncodeContext *pMe, const char *szLabel, uint8_t uTag, int64_t nDate)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_EPOCH);
-   QCBOREncode_AddInt64(pCtx, date);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTDateEpoch(pMe, uTag, nDate);
 }
 
-static inline void QCBOREncode_AddDateEpochToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t date)
+static inline void
+QCBOREncode_AddTDateEpochToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTag, int64_t nDate)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_EPOCH);
-   QCBOREncode_AddInt64(pCtx, date);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTDateEpoch(pMe, uTag, nDate);
 }
 
-
-static inline void QCBOREncode_AddBytes(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddDateEpoch(QCBOREncodeContext *pMe, int64_t nDate)
 {
-   QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING, Bytes);
+   QCBOREncode_AddTDateEpoch(pMe, QCBOR_ENCODE_AS_TAG, nDate);
 }
 
-static inline void QCBOREncode_AddBytesToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddDateEpochToMap(QCBOREncodeContext *pMe, const char *szLabel, int64_t nDate)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddDateEpoch(pMe, nDate);
 }
 
-static inline void QCBOREncode_AddBytesToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddDateEpochToMapN(QCBOREncodeContext *pMe, int64_t nLabel, int64_t nDate)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddDateEpoch(pMe, nDate);
 }
 
-static inline void QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+
+
+static inline void
+QCBOREncode_AddBytes(QCBOREncodeContext *pMe, UsefulBufC Bytes)
 {
-    QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY, Bytes);
+   QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, Bytes);
 }
 
-static inline void QCBOREncode_AddBytesLenOnlyToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBytesToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Bytes)
 {
-    QCBOREncode_AddSZString(pCtx, szLabel);
-    QCBOREncode_AddBytesLenOnly(pCtx, Bytes);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddBytes(pMe, Bytes);
 }
 
-static inline void QCBOREncode_AddBytesLenOnlyToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBytesToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Bytes)
 {
-    QCBOREncode_AddInt64(pCtx, nLabel);
-    QCBOREncode_AddBytesLenOnly(pCtx, Bytes);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddBytes(pMe, Bytes);
 }
 
-static inline void QCBOREncode_AddBinaryUUID(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pMe, UsefulBufC Bytes)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_BIN_UUID);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+    QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY, Bytes);
 }
 
-static inline void QCBOREncode_AddBinaryUUIDToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBytesLenOnlyToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Bytes)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_BIN_UUID);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+    QCBOREncode_AddSZString(pMe, szLabel);
+    QCBOREncode_AddBytesLenOnly(pMe, Bytes);
 }
 
-static inline void QCBOREncode_AddBinaryUUIDToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBytesLenOnlyToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Bytes)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_BIN_UUID);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+    QCBOREncode_AddInt64(pMe, nLabel);
+    QCBOREncode_AddBytesLenOnly(pMe, Bytes);
 }
 
 
-static inline void QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddTBinaryUUID(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC Bytes)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_POS_BIGNUM);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_BIN_UUID);
+   }
+   QCBOREncode_AddBytes(pMe, Bytes);
 }
 
-static inline void QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddTBinaryUUIDToMapSZ(QCBOREncodeContext *pMe,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC          Bytes)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_POS_BIGNUM);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTBinaryUUID(pMe, uTagRequirement, Bytes);
 }
 
-static inline void QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddTBinaryUUIDToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC Bytes)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_POS_BIGNUM);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTBinaryUUID(pMe, uTagRequirement, Bytes);
 }
 
-
-static inline void QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBinaryUUID(QCBOREncodeContext *pMe, UsefulBufC Bytes)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_NEG_BIGNUM);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddTBinaryUUID(pMe, QCBOR_ENCODE_AS_TAG, Bytes);
 }
 
-static inline void QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBinaryUUIDToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Bytes)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_NEG_BIGNUM);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddTBinaryUUIDToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, Bytes);
 }
 
-static inline void QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddBinaryUUIDToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Bytes)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_NEG_BIGNUM);
-   QCBOREncode_AddBytes(pCtx, Bytes);
+   QCBOREncode_AddTBinaryUUIDToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, Bytes);
 }
 
 
+static inline void
+QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC Bytes)
+{
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_POS_BIGNUM);
+   }
+   QCBOREncode_AddBytes(pMe, Bytes);
+}
+
+static inline void
+QCBOREncode_AddTPositiveBignumToMapSZ(QCBOREncodeContext *pMe,
+                                      const char         *szLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC          Bytes)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTPositiveBignum(pMe, uTagRequirement, Bytes);
+}
+
+static inline void
+QCBOREncode_AddTPositiveBignumToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC Bytes)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTPositiveBignum(pMe, uTagRequirement, Bytes);
+}
+
+static inline void
+QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pMe, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTPositiveBignum(pMe, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+static inline void
+QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTPositiveBignumToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+static inline void
+QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTPositiveBignumToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+
+static inline void
+QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC Bytes)
+{
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_NEG_BIGNUM);
+   }
+   QCBOREncode_AddBytes(pMe, Bytes);
+}
+
+static inline void
+QCBOREncode_AddTNegativeBignumToMapSZ(QCBOREncodeContext *pMe,
+                                      const char         *szLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC          Bytes)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTNegativeBignum(pMe, uTagRequirement, Bytes);
+}
+
+static inline void
+QCBOREncode_AddTNegativeBignumToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC Bytes)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTNegativeBignum(pMe, uTagRequirement, Bytes);
+}
+
+static inline void
+QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pMe, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTNegativeBignum(pMe, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+static inline void
+QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTNegativeBignumToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+static inline void
+QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTNegativeBignumToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+
+
 #ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
 
-static inline void QCBOREncode_AddDecimalFraction(QCBOREncodeContext *pCtx,
-                                                  int64_t             nMantissa,
-                                                  int64_t             nBase10Exponent)
+static inline void
+QCBOREncode_AddTDecimalFraction(QCBOREncodeContext *pMe,
+                                uint8_t             uTagRequirement,
+                                int64_t             nMantissa,
+                                int64_t             nBase10Exponent)
 {
-   QCBOREncode_AddExponentAndMantissa(pCtx,
-                                      CBOR_TAG_DECIMAL_FRACTION,
+   uint64_t uTag;
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      uTag = CBOR_TAG_DECIMAL_FRACTION;
+   } else {
+      uTag = CBOR_TAG_INVALID64;
+   }
+   QCBOREncode_AddExponentAndMantissa(pMe,
+                                      uTag,
                                       NULLUsefulBufC,
                                       false,
                                       nMantissa,
                                       nBase10Exponent);
 }
 
-static inline void QCBOREncode_AddDecimalFractionToMap(QCBOREncodeContext *pCtx,
-                                                       const char         *szLabel,
-                                                       int64_t             nMantissa,
-                                                       int64_t             nBase10Exponent)
+static inline void
+QCBOREncode_AddTDecimalFractionToMapSZ(QCBOREncodeContext *pMe,
+                                       const char         *szLabel,
+                                       uint8_t             uTagRequirement,
+                                       int64_t             nMantissa,
+                                       int64_t             nBase10Exponent)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddDecimalFraction(pCtx, nMantissa, nBase10Exponent);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTDecimalFraction(pMe, uTagRequirement, nMantissa, nBase10Exponent);
 }
 
-static inline void QCBOREncode_AddDecimalFractionToMapN(QCBOREncodeContext *pCtx,
-                                                        int64_t             nLabel,
-                                                        int64_t             nMantissa,
-                                                        int64_t             nBase10Exponent)
+static inline void
+QCBOREncode_AddTDecimalFractionToMapN(QCBOREncodeContext *pMe,
+                                      int64_t             nLabel,
+                                      uint8_t             uTagRequirement,
+                                      int64_t             nMantissa,
+                                      int64_t             nBase10Exponent)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddDecimalFraction(pCtx, nMantissa, nBase10Exponent);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTDecimalFraction(pMe, uTagRequirement, nMantissa, nBase10Exponent);
 }
 
-static inline void QCBOREncode_AddDecimalFractionBigNum(QCBOREncodeContext *pCtx,
-                                                        UsefulBufC          Mantissa,
-                                                        bool                bIsNegative,
-                                                        int64_t             nBase10Exponent)
+static inline void
+QCBOREncode_AddDecimalFraction(QCBOREncodeContext *pMe,
+                               int64_t             nMantissa,
+                               int64_t             nBase10Exponent)
 {
-   QCBOREncode_AddExponentAndMantissa(pCtx,
-                                      CBOR_TAG_DECIMAL_FRACTION,
+   QCBOREncode_AddTDecimalFraction(pMe, QCBOR_ENCODE_AS_TAG, nMantissa, nBase10Exponent);
+}
+
+static inline void
+QCBOREncode_AddDecimalFractionToMap(QCBOREncodeContext *pMe,
+                                    const char         *szLabel,
+                                    int64_t             nMantissa,
+                                    int64_t             nBase10Exponent)
+{
+   QCBOREncode_AddTDecimalFractionToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, nMantissa, nBase10Exponent);
+}
+
+static inline void
+QCBOREncode_AddDecimalFractionToMapN(QCBOREncodeContext *pMe,
+                                     int64_t             nLabel,
+                                     int64_t             nMantissa,
+                                     int64_t             nBase10Exponent)
+{
+   QCBOREncode_AddTDecimalFractionToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, nMantissa, nBase10Exponent);
+}
+
+
+
+static inline void
+QCBOREncode_AddTDecimalFractionBigNum(QCBOREncodeContext *pMe,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC          Mantissa,
+                                      bool                bIsNegative,
+                                      int64_t             nBase10Exponent)
+{
+   uint64_t uTag;
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      uTag = CBOR_TAG_DECIMAL_FRACTION;
+   } else {
+      uTag = CBOR_TAG_INVALID64;
+   }
+   QCBOREncode_AddExponentAndMantissa(pMe,
+                                      uTag,
                                       Mantissa, bIsNegative,
                                       0,
                                       nBase10Exponent);
 }
 
-static inline void QCBOREncode_AddDecimalFractionBigNumToMap(QCBOREncodeContext *pCtx,
-                                                             const char         *szLabel,
-                                                             UsefulBufC          Mantissa,
-                                                             bool                bIsNegative,
-                                                             int64_t             nBase10Exponent)
+static inline void
+QCBOREncode_AddTDecimalFractionBigNumToMapSZ(QCBOREncodeContext *pMe,
+                                             const char         *szLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBufC          Mantissa,
+                                             bool                bIsNegative,
+                                             int64_t             nBase10Exponent)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddDecimalFractionBigNum(pCtx, Mantissa, bIsNegative, nBase10Exponent);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTDecimalFractionBigNum(pMe, uTagRequirement, Mantissa, bIsNegative, nBase10Exponent);
 }
 
-static inline void QCBOREncode_AddDecimalFractionBigNumToMapN(QCBOREncodeContext *pCtx,
-                                                              int64_t             nLabel,
-                                                              UsefulBufC          Mantissa,
-                                                              bool                bIsNegative,
-                                                              int64_t             nBase2Exponent)
+static inline void
+QCBOREncode_AddTDecimalFractionBigNumToMapN(QCBOREncodeContext *pMe,
+                                            int64_t             nLabel,
+                                            uint8_t             uTagRequirement,
+                                            UsefulBufC          Mantissa,
+                                            bool                bIsNegative,
+                                            int64_t             nBase10Exponent)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddDecimalFractionBigNum(pCtx, Mantissa, bIsNegative, nBase2Exponent);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTDecimalFractionBigNum(pMe, uTagRequirement, Mantissa, bIsNegative, nBase10Exponent);
 }
 
-static inline void QCBOREncode_AddBigFloat(QCBOREncodeContext *pCtx,
-                                           int64_t             nMantissa,
+static inline void
+QCBOREncode_AddDecimalFractionBigNum(QCBOREncodeContext *pMe,
+                                     UsefulBufC          Mantissa,
+                                     bool                bIsNegative,
+                                     int64_t             nBase10Exponent)
+{
+   QCBOREncode_AddTDecimalFractionBigNum(pMe, QCBOR_ENCODE_AS_TAG, Mantissa, bIsNegative, nBase10Exponent);
+}
+
+static inline void
+QCBOREncode_AddDecimalFractionBigNumToMapSZ(QCBOREncodeContext *pMe,
+                                            const char         *szLabel,
+                                            UsefulBufC          Mantissa,
+                                            bool                bIsNegative,
+                                            int64_t             nBase10Exponent)
+{
+   QCBOREncode_AddTDecimalFractionBigNumToMapSZ(pMe,
+                                                szLabel,
+                                                QCBOR_ENCODE_AS_TAG,
+                                                Mantissa,
+                                                bIsNegative,
+                                                nBase10Exponent);
+}
+
+static inline void
+QCBOREncode_AddDecimalFractionBigNumToMapN(QCBOREncodeContext *pMe,
+                                           int64_t             nLabel,
+                                           UsefulBufC          Mantissa,
+                                           bool                bIsNegative,
                                            int64_t             nBase2Exponent)
 {
-   QCBOREncode_AddExponentAndMantissa(pCtx, CBOR_TAG_BIGFLOAT, NULLUsefulBufC, false, nMantissa, nBase2Exponent);
+   QCBOREncode_AddTDecimalFractionBigNumToMapN(pMe,
+                                               nLabel,
+                                               QCBOR_ENCODE_AS_TAG,
+                                               Mantissa,
+                                               bIsNegative,
+                                               nBase2Exponent);
 }
 
-static inline void QCBOREncode_AddBigFloatToMap(QCBOREncodeContext *pCtx,
-                                                const char         *szLabel,
-                                                int64_t             nMantissa,
-                                                int64_t             nBase2Exponent)
+
+
+
+
+static inline void
+QCBOREncode_AddTBigFloat(QCBOREncodeContext *pMe,
+                         uint8_t             uTagRequirement,
+                         int64_t             nMantissa,
+                         int64_t             nBase2Exponent)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddBigFloat(pCtx, nMantissa, nBase2Exponent);
+   uint64_t uTag;
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      uTag = CBOR_TAG_BIGFLOAT;
+   } else {
+      uTag = CBOR_TAG_INVALID64;
+   }
+   QCBOREncode_AddExponentAndMantissa(pMe, uTag, NULLUsefulBufC, false, nMantissa, nBase2Exponent);
 }
 
-static inline void QCBOREncode_AddBigFloatToMapN(QCBOREncodeContext *pCtx,
-                                                 int64_t             nLabel,
-                                                 int64_t             nMantissa,
-                                                 int64_t             nBase2Exponent)
+static inline void
+QCBOREncode_AddTBigFloatToMapSZ(QCBOREncodeContext *pMe,
+                                const char         *szLabel,
+                                uint8_t             uTagRequirement,
+                                int64_t             nMantissa,
+                                int64_t             nBase2Exponent)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddBigFloat(pCtx, nMantissa, nBase2Exponent);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTBigFloat(pMe, uTagRequirement, nMantissa, nBase2Exponent);
 }
 
-static inline void QCBOREncode_AddBigFloatBigNum(QCBOREncodeContext *pCtx,
-                                                 UsefulBufC          Mantissa,
-                                                 bool                bIsNegative,
-                                                 int64_t             nBase2Exponent)
+static inline void
+QCBOREncode_AddTBigFloatToMapN(QCBOREncodeContext *pMe,
+                               int64_t             nLabel,
+                               uint8_t             uTagRequirement,
+                               int64_t             nMantissa,
+                               int64_t             nBase2Exponent)
 {
-   QCBOREncode_AddExponentAndMantissa(pCtx, CBOR_TAG_BIGFLOAT, Mantissa, bIsNegative, 0, nBase2Exponent);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTBigFloat(pMe, uTagRequirement, nMantissa, nBase2Exponent);
 }
 
-static inline void QCBOREncode_AddBigFloatBigNumToMap(QCBOREncodeContext *pCtx,
-                                                      const char         *szLabel,
-                                                      UsefulBufC          Mantissa,
-                                                      bool                bIsNegative,
-                                                      int64_t             nBase2Exponent)
+static inline void
+QCBOREncode_AddBigFloat(QCBOREncodeContext *pMe,
+                        int64_t             nMantissa,
+                        int64_t             nBase2Exponent)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddBigFloatBigNum(pCtx, Mantissa, bIsNegative, nBase2Exponent);
+   QCBOREncode_AddTBigFloat(pMe, QCBOR_ENCODE_AS_TAG, nMantissa, nBase2Exponent);
 }
 
-static inline void QCBOREncode_AddBigFloatBigNumToMapN(QCBOREncodeContext *pCtx,
-                                                       int64_t             nLabel,
-                                                       UsefulBufC          Mantissa,
-                                                       bool                bIsNegative,
-                                                       int64_t             nBase2Exponent)
+static inline void
+QCBOREncode_AddBigFloatToMap(QCBOREncodeContext *pMe,
+                             const char         *szLabel,
+                             int64_t             nMantissa,
+                             int64_t             nBase2Exponent)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddBigFloatBigNum(pCtx, Mantissa, bIsNegative, nBase2Exponent);
+   QCBOREncode_AddTBigFloatToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, nMantissa, nBase2Exponent);
+}
+
+static inline void
+QCBOREncode_AddBigFloatToMapN(QCBOREncodeContext *pMe,
+                              int64_t             nLabel,
+                              int64_t             nMantissa,
+                              int64_t             nBase2Exponent)
+{
+   QCBOREncode_AddTBigFloatToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, nMantissa, nBase2Exponent);
+}
+
+
+
+static inline void
+QCBOREncode_AddTBigFloatBigNum(QCBOREncodeContext *pMe,
+                               uint8_t             uTagRequirement,
+                               UsefulBufC          Mantissa,
+                               bool                bIsNegative,
+                               int64_t             nBase2Exponent)
+{
+   uint64_t uTag;
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      uTag = CBOR_TAG_BIGFLOAT;
+   } else {
+      uTag = CBOR_TAG_INVALID64;
+   }
+   QCBOREncode_AddExponentAndMantissa(pMe, uTag, Mantissa, bIsNegative, 0, nBase2Exponent);
+}
+
+static inline void
+QCBOREncode_AddTBigFloatBigNumToMapSZ(QCBOREncodeContext *pMe,
+                                      const char         *szLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC          Mantissa,
+                                      bool                bIsNegative,
+                                      int64_t             nBase2Exponent)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTBigFloatBigNum(pMe, uTagRequirement, Mantissa, bIsNegative, nBase2Exponent);
+}
+
+static inline void
+QCBOREncode_AddTBigFloatBigNumToMapN(QCBOREncodeContext *pMe,
+                                     int64_t             nLabel,
+                                     uint8_t             uTagRequirement,
+                                     UsefulBufC          Mantissa,
+                                     bool                bIsNegative,
+                                     int64_t             nBase2Exponent)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTBigFloatBigNum(pMe, uTagRequirement, Mantissa, bIsNegative, nBase2Exponent);
+}
+
+
+static inline void
+QCBOREncode_AddBigFloatBigNum(QCBOREncodeContext *pMe,
+                              UsefulBufC          Mantissa,
+                              bool                bIsNegative,
+                              int64_t             nBase2Exponent)
+{
+   QCBOREncode_AddTBigFloatBigNum(pMe, QCBOR_ENCODE_AS_TAG, Mantissa, bIsNegative, nBase2Exponent);
+}
+
+static inline void
+QCBOREncode_AddBigFloatBigNumToMap(QCBOREncodeContext *pMe,
+                                   const char         *szLabel,
+                                   UsefulBufC          Mantissa,
+                                   bool                bIsNegative,
+                                   int64_t             nBase2Exponent)
+{
+   QCBOREncode_AddTBigFloatBigNumToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, Mantissa, bIsNegative, nBase2Exponent);
+}
+
+static inline void
+QCBOREncode_AddBigFloatBigNumToMapN(QCBOREncodeContext *pMe,
+                                    int64_t             nLabel,
+                                    UsefulBufC          Mantissa,
+                                    bool                bIsNegative,
+                                    int64_t             nBase2Exponent)
+{
+   QCBOREncode_AddTBigFloatBigNumToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, Mantissa, bIsNegative, nBase2Exponent);
 }
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 
 
-static inline void QCBOREncode_AddURI(QCBOREncodeContext *pCtx, UsefulBufC URI)
+static inline void
+QCBOREncode_AddTURI(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC URI)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_URI);
-   QCBOREncode_AddText(pCtx, URI);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_URI);
+   }
+   QCBOREncode_AddText(pMe, URI);
 }
 
-static inline void QCBOREncode_AddURIToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC URI)
+static inline void
+QCBOREncode_AddTURIToMapSZ(QCBOREncodeContext *pMe, const char *szLabel, uint8_t uTagRequirement, UsefulBufC URI)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_URI);
-   QCBOREncode_AddText(pCtx, URI);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTURI(pMe, uTagRequirement, URI);
 }
 
-static inline void QCBOREncode_AddURIToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC URI)
+static inline void
+QCBOREncode_AddTURIToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC URI)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_URI);
-   QCBOREncode_AddText(pCtx, URI);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTURI(pMe, uTagRequirement, URI);
+}
+
+static inline void
+QCBOREncode_AddURI(QCBOREncodeContext *pMe, UsefulBufC URI)
+{
+   QCBOREncode_AddTURI(pMe, QCBOR_ENCODE_AS_TAG, URI);
+}
+
+static inline void
+QCBOREncode_AddURIToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC URI)
+{
+   QCBOREncode_AddTURIToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, URI);
+}
+
+static inline void
+QCBOREncode_AddURIToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC URI)
+{
+   QCBOREncode_AddTURIToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, URI);
 }
 
 
 
-static inline void QCBOREncode_AddB64Text(QCBOREncodeContext *pCtx, UsefulBufC B64Text)
+static inline void
+QCBOREncode_AddTB64Text(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC B64Text)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_B64);
-   QCBOREncode_AddText(pCtx, B64Text);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_B64);
+   }
+   QCBOREncode_AddText(pMe, B64Text);
 }
 
-static inline void QCBOREncode_AddB64TextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text)
+static inline void
+QCBOREncode_AddTB64TextToMapSZ(QCBOREncodeContext *pMe,
+                               const char         *szLabel,
+                               uint8_t             uTagRequirement,
+                               UsefulBufC          B64Text)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_B64);
-   QCBOREncode_AddText(pCtx, B64Text);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTB64Text(pMe, uTagRequirement, B64Text);
 }
 
-static inline void QCBOREncode_AddB64TextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text)
+static inline void
+QCBOREncode_AddTB64TextToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC B64Text)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_B64);
-   QCBOREncode_AddText(pCtx, B64Text);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTB64Text(pMe, uTagRequirement, B64Text);
+}
+
+static inline void
+QCBOREncode_AddB64Text(QCBOREncodeContext *pMe, UsefulBufC B64Text)
+{
+   QCBOREncode_AddTB64Text(pMe, QCBOR_ENCODE_AS_TAG, B64Text);
+}
+
+static inline void
+QCBOREncode_AddB64TextToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC B64Text)
+{
+   QCBOREncode_AddTB64TextToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, B64Text);
+}
+
+static inline void
+QCBOREncode_AddB64TextToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC B64Text)
+{
+   QCBOREncode_AddTB64TextToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, B64Text);
 }
 
 
-static inline void QCBOREncode_AddB64URLText(QCBOREncodeContext *pCtx, UsefulBufC B64Text)
+
+static inline void
+QCBOREncode_AddTB64URLText(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC B64Text)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_B64URL);
-   QCBOREncode_AddText(pCtx, B64Text);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_B64URL);
+   }
+   QCBOREncode_AddText(pMe, B64Text);
 }
 
-static inline void QCBOREncode_AddB64URLTextToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC B64Text)
+static inline void
+QCBOREncode_AddTB64URLTextToMapSZ(QCBOREncodeContext *pMe,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC          B64Text)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_B64URL);
-   QCBOREncode_AddText(pCtx, B64Text);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTB64URLText(pMe, uTagRequirement, B64Text);
 }
 
-static inline void QCBOREncode_AddB64URLTextToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC B64Text)
+static inline void
+QCBOREncode_AddTB64URLTextToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC B64Text)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_B64URL);
-   QCBOREncode_AddText(pCtx, B64Text);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTB64URLText(pMe, uTagRequirement, B64Text);
+}
+
+static inline void
+QCBOREncode_AddB64URLText(QCBOREncodeContext *pMe, UsefulBufC B64Text)
+{
+   QCBOREncode_AddTB64URLText(pMe, QCBOR_ENCODE_AS_TAG, B64Text);
+}
+
+static inline void
+QCBOREncode_AddB64URLTextToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC B64Text)
+{
+   QCBOREncode_AddTB64URLTextToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, B64Text);
+}
+
+static inline void
+QCBOREncode_AddB64URLTextToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC B64Text)
+{
+   QCBOREncode_AddTB64URLTextToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, B64Text);
 }
 
 
-static inline void QCBOREncode_AddRegex(QCBOREncodeContext *pCtx, UsefulBufC Bytes)
+
+static inline void
+QCBOREncode_AddTRegex(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC Bytes)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_REGEX);
-   QCBOREncode_AddText(pCtx, Bytes);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_REGEX);
+   }
+   QCBOREncode_AddText(pMe, Bytes);
 }
 
-static inline void QCBOREncode_AddRegexToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddTRegexToMapSZ(QCBOREncodeContext *pMe, const char *szLabel, uint8_t uTagRequirement, UsefulBufC Bytes)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_REGEX);
-   QCBOREncode_AddText(pCtx, Bytes);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTRegex(pMe, uTagRequirement, Bytes);
 }
 
-static inline void QCBOREncode_AddRegexToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Bytes)
+static inline void
+QCBOREncode_AddTRegexToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC Bytes)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_REGEX);
-   QCBOREncode_AddText(pCtx, Bytes);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTRegex(pMe, uTagRequirement, Bytes);
+}
+
+static inline void
+QCBOREncode_AddRegex(QCBOREncodeContext *pMe, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTRegex(pMe, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+static inline void
+QCBOREncode_AddRegexToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTRegexToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, Bytes);
+}
+
+static inline void
+QCBOREncode_AddRegexToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Bytes)
+{
+   QCBOREncode_AddTRegexToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, Bytes);
+
 }
 
 
-static inline void QCBOREncode_AddMIMEData(QCBOREncodeContext *pCtx, UsefulBufC MIMEData)
+static inline void
+QCBOREncode_AddTMIMEData(QCBOREncodeContext *pMe, uint8_t uTagRequirement, UsefulBufC MIMEData)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_MIME);
-   QCBOREncode_AddText(pCtx, MIMEData);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_BINARY_MIME);
+   }
+   QCBOREncode_AddBytes(pMe, MIMEData);
 }
 
-static inline void QCBOREncode_AddMIMEDataToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC MIMEData)
+static inline void
+QCBOREncode_AddTMIMEDataToMapSZ(QCBOREncodeContext *pMe,
+                                const char         *szLabel,
+                                uint8_t             uTagRequirement,
+                                UsefulBufC          MIMEData)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_MIME);
-   QCBOREncode_AddText(pCtx, MIMEData);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTMIMEData(pMe, uTagRequirement, MIMEData);
 }
 
-static inline void QCBOREncode_AddMIMEDataToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC MIMEData)
+static inline void
+QCBOREncode_AddTMIMEDataToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, UsefulBufC MIMEData)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_MIME);
-   QCBOREncode_AddText(pCtx, MIMEData);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTMIMEData(pMe, uTagRequirement, MIMEData);
+}
+
+static inline void
+QCBOREncode_AddMIMEData(QCBOREncodeContext *pMe, UsefulBufC MIMEData)
+{
+   QCBOREncode_AddTMIMEData(pMe, QCBOR_ENCODE_AS_TAG, MIMEData);
+}
+
+static inline void
+QCBOREncode_AddMIMEDataToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC MIMEData)
+{
+   QCBOREncode_AddTMIMEDataToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, MIMEData);
+}
+
+static inline void
+QCBOREncode_AddMIMEDataToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC MIMEData)
+{
+   QCBOREncode_AddTMIMEDataToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, MIMEData);
 }
 
 
-static inline void QCBOREncode_AddDateString(QCBOREncodeContext *pCtx, const char *szDate)
+static inline void
+QCBOREncode_AddTDateString(QCBOREncodeContext *pMe, uint8_t uTagRequirement, const char *szDate)
 {
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_STRING);
-   QCBOREncode_AddSZString(pCtx, szDate);
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_DATE_STRING);
+   }
+   QCBOREncode_AddSZString(pMe, szDate);
 }
 
-static inline void QCBOREncode_AddDateStringToMap(QCBOREncodeContext *pCtx, const char *szLabel, const char *szDate)
+static inline void
+QCBOREncode_AddTDateStringToMapSZ(QCBOREncodeContext *pMe,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  const char         *szDate)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_STRING);
-   QCBOREncode_AddSZString(pCtx, szDate);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTDateString(pMe, uTagRequirement, szDate);
 }
 
-static inline void QCBOREncode_AddDateStringToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, const char *szDate)
+static inline void
+QCBOREncode_AddTDateStringToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, const char *szDate)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddTag(pCtx, CBOR_TAG_DATE_STRING);
-   QCBOREncode_AddSZString(pCtx, szDate);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTDateString(pMe, uTagRequirement, szDate);
+}
+
+static inline void
+QCBOREncode_AddDateString(QCBOREncodeContext *pMe, const char *szDate)
+{
+   QCBOREncode_AddTDateString(pMe, QCBOR_ENCODE_AS_TAG, szDate);
+}
+
+static inline void
+QCBOREncode_AddDateStringToMap(QCBOREncodeContext *pMe, const char *szLabel, const char *szDate)
+{
+   QCBOREncode_AddTDateStringToMapSZ(pMe, szLabel, QCBOR_ENCODE_AS_TAG, szDate);
+}
+
+static inline void
+QCBOREncode_AddDateStringToMapN(QCBOREncodeContext *pMe, int64_t nLabel, const char *szDate)
+{
+   QCBOREncode_AddTDateStringToMapN(pMe, nLabel, QCBOR_ENCODE_AS_TAG, szDate);
 }
 
 
-static inline void QCBOREncode_AddSimple(QCBOREncodeContext *pCtx, uint64_t uNum)
+static inline void
+QCBOREncode_AddSimple(QCBOREncodeContext *pMe, uint64_t uNum)
 {
-   QCBOREncode_AddType7(pCtx, 0, uNum);
+   QCBOREncode_AddType7(pMe, 0, uNum);
 }
 
-static inline void QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pCtx, const char *szLabel, uint8_t uSimple)
+static inline void
+QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pMe, const char *szLabel, uint8_t uSimple)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddSimple(pCtx, uSimple);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddSimple(pMe, uSimple);
 }
 
-static inline void QCBOREncode_AddSimpleToMapN(QCBOREncodeContext *pCtx, int nLabel, uint8_t uSimple)
+static inline void
+QCBOREncode_AddSimpleToMapN(QCBOREncodeContext *pMe, int nLabel, uint8_t uSimple)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddSimple(pCtx, uSimple);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddSimple(pMe, uSimple);
 }
 
 
-static inline void QCBOREncode_AddBool(QCBOREncodeContext *pCtx, bool b)
+static inline void
+QCBOREncode_AddBool(QCBOREncodeContext *pMe, bool b)
 {
    uint8_t uSimple = CBOR_SIMPLEV_FALSE;
    if(b) {
       uSimple = CBOR_SIMPLEV_TRUE;
    }
-   QCBOREncode_AddSimple(pCtx, uSimple);
+   QCBOREncode_AddSimple(pMe, uSimple);
 }
 
-static inline void QCBOREncode_AddBoolToMap(QCBOREncodeContext *pCtx, const char *szLabel, bool b)
+static inline void
+QCBOREncode_AddBoolToMap(QCBOREncodeContext *pMe, const char *szLabel, bool b)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddBool(pCtx, b);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddBool(pMe, b);
 }
 
-static inline void QCBOREncode_AddBoolToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, bool b)
+static inline void
+QCBOREncode_AddBoolToMapN(QCBOREncodeContext *pMe, int64_t nLabel, bool b)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddBool(pCtx, b);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddBool(pMe, b);
 }
 
 
-static inline void QCBOREncode_AddNULL(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_AddNULL(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_AddSimple(pCtx, CBOR_SIMPLEV_NULL);
+   QCBOREncode_AddSimple(pMe, CBOR_SIMPLEV_NULL);
 }
 
-static inline void QCBOREncode_AddNULLToMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_AddNULLToMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddNULL(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddNULL(pMe);
 }
 
-static inline void QCBOREncode_AddNULLToMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+static inline void
+QCBOREncode_AddNULLToMapN(QCBOREncodeContext *pMe, int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddNULL(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddNULL(pMe);
 }
 
 
-static inline void QCBOREncode_AddUndef(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_AddUndef(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_AddSimple(pCtx, CBOR_SIMPLEV_UNDEF);
+   QCBOREncode_AddSimple(pMe, CBOR_SIMPLEV_UNDEF);
 }
 
-static inline void QCBOREncode_AddUndefToMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_AddUndefToMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddUndef(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddUndef(pMe);
 }
 
-static inline void QCBOREncode_AddUndefToMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+static inline void
+QCBOREncode_AddUndefToMapN(QCBOREncodeContext *pMe, int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddUndef(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddUndef(pMe);
 }
 
 
-static inline void QCBOREncode_OpenArray(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_OpenArray(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_ARRAY);
+   QCBOREncode_OpenMapOrArray(pMe, CBOR_MAJOR_TYPE_ARRAY);
 }
 
-static inline void QCBOREncode_OpenArrayInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_OpenArrayInMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_OpenArray(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_OpenArray(pMe);
 }
 
-static inline void QCBOREncode_OpenArrayInMapN(QCBOREncodeContext *pCtx,  int64_t nLabel)
+static inline void
+QCBOREncode_OpenArrayInMapN(QCBOREncodeContext *pMe,  int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_OpenArray(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_OpenArray(pMe);
 }
 
-static inline void QCBOREncode_CloseArray(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_CloseArray(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_ARRAY);
+   QCBOREncode_CloseMapOrArray(pMe, CBOR_MAJOR_TYPE_ARRAY);
 }
 
 
-static inline void QCBOREncode_OpenMap(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_OpenMap(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_MAP);
+   QCBOREncode_OpenMapOrArray(pMe, CBOR_MAJOR_TYPE_MAP);
 }
 
-static inline void QCBOREncode_OpenMapInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_OpenMapInMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_OpenMap(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_OpenMap(pMe);
 }
 
-static inline void QCBOREncode_OpenMapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+static inline void
+QCBOREncode_OpenMapInMapN(QCBOREncodeContext *pMe, int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_OpenMap(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_OpenMap(pMe);
 }
 
-static inline void QCBOREncode_CloseMap(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_CloseMap(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_MAP);
+   QCBOREncode_CloseMapOrArray(pMe, CBOR_MAJOR_TYPE_MAP);
 }
 
-static inline void QCBOREncode_OpenArrayIndefiniteLength(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_OpenArrayIndefiniteLength(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_OpenMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN);
+   QCBOREncode_OpenMapOrArrayIndefiniteLength(pMe, CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN);
 }
 
-static inline void QCBOREncode_OpenArrayIndefiniteLengthInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_OpenArrayIndefiniteLengthInMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_OpenArrayIndefiniteLength(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_OpenArrayIndefiniteLength(pMe);
 }
 
-static inline void QCBOREncode_OpenArrayIndefiniteLengthInMapN(QCBOREncodeContext *pCtx,  int64_t nLabel)
+static inline void
+QCBOREncode_OpenArrayIndefiniteLengthInMapN(QCBOREncodeContext *pMe,  int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_OpenArrayIndefiniteLength(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_OpenArrayIndefiniteLength(pMe);
 }
 
-static inline void QCBOREncode_CloseArrayIndefiniteLength(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_CloseArrayIndefiniteLength(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_CloseMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN);
+   QCBOREncode_CloseMapOrArrayIndefiniteLength(pMe, CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN);
 }
 
 
-static inline void QCBOREncode_OpenMapIndefiniteLength(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_OpenMapIndefiniteLength(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_OpenMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
+   QCBOREncode_OpenMapOrArrayIndefiniteLength(pMe, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
 }
 
-static inline void QCBOREncode_OpenMapIndefiniteLengthInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_OpenMapIndefiniteLengthInMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_OpenMapIndefiniteLength(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_OpenMapIndefiniteLength(pMe);
 }
 
-static inline void QCBOREncode_OpenMapIndefiniteLengthInMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+static inline void
+QCBOREncode_OpenMapIndefiniteLengthInMapN(QCBOREncodeContext *pMe, int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_OpenMapIndefiniteLength(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_OpenMapIndefiniteLength(pMe);
 }
 
-static inline void QCBOREncode_CloseMapIndefiniteLength(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_CloseMapIndefiniteLength(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_CloseMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
+   QCBOREncode_CloseMapOrArrayIndefiniteLength(pMe, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
 }
 
 
-static inline void QCBOREncode_BstrWrap(QCBOREncodeContext *pCtx)
+static inline void
+QCBOREncode_BstrWrap(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING);
+   QCBOREncode_OpenMapOrArray(pMe, CBOR_MAJOR_TYPE_BYTE_STRING);
 }
 
-static inline void QCBOREncode_BstrWrapInMap(QCBOREncodeContext *pCtx, const char *szLabel)
+static inline void
+QCBOREncode_BstrWrapInMap(QCBOREncodeContext *pMe, const char *szLabel)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_BstrWrap(pCtx);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_BstrWrap(pMe);
 }
 
-static inline void QCBOREncode_BstrWrapInMapN(QCBOREncodeContext *pCtx, int64_t nLabel)
+static inline void
+QCBOREncode_BstrWrapInMapN(QCBOREncodeContext *pMe, int64_t nLabel)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_BstrWrap(pCtx);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_BstrWrap(pMe);
 }
 
-static inline void QCBOREncode_CloseBstrWrap(QCBOREncodeContext *pCtx, UsefulBufC *pWrappedCBOR)
+static inline void
+QCBOREncode_CloseBstrWrap(QCBOREncodeContext *pMe, UsefulBufC *pWrappedCBOR)
 {
-   QCBOREncode_CloseBstrWrap2(pCtx, true, pWrappedCBOR);
+   QCBOREncode_CloseBstrWrap2(pMe, true, pWrappedCBOR);
 }
 
 
-static inline void QCBOREncode_AddEncoded(QCBOREncodeContext *pCtx, UsefulBufC Encoded)
+static inline void
+QCBOREncode_AddEncoded(QCBOREncodeContext *pMe, UsefulBufC Encoded)
 {
-   QCBOREncode_AddBuffer(pCtx, CBOR_MAJOR_NONE_TYPE_RAW, Encoded);
+   QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_NONE_TYPE_RAW, Encoded);
 }
 
-static inline void QCBOREncode_AddEncodedToMap(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBufC Encoded)
+static inline void
+QCBOREncode_AddEncodedToMap(QCBOREncodeContext *pMe, const char *szLabel, UsefulBufC Encoded)
 {
-   QCBOREncode_AddSZString(pCtx, szLabel);
-   QCBOREncode_AddEncoded(pCtx, Encoded);
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddEncoded(pMe, Encoded);
 }
 
-static inline void QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBufC Encoded)
+static inline void
+QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBufC Encoded)
 {
-   QCBOREncode_AddInt64(pCtx, nLabel);
-   QCBOREncode_AddEncoded(pCtx, Encoded);
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddEncoded(pMe, Encoded);
 }
 
 
-static inline int QCBOREncode_IsBufferNULL(QCBOREncodeContext *pCtx)
+static inline int
+QCBOREncode_IsBufferNULL(QCBOREncodeContext *pMe)
 {
-   return UsefulOutBuf_IsBufferNULL(&(pCtx->OutBuf));
+   return UsefulOutBuf_IsBufferNULL(&(pMe->OutBuf));
 }
 
-static inline QCBORError QCBOREncode_GetErrorState(QCBOREncodeContext *pCtx)
+static inline QCBORError
+QCBOREncode_GetErrorState(QCBOREncodeContext *pMe)
 {
-   if(UsefulOutBuf_GetError(&(pCtx->OutBuf))) {
+   if(UsefulOutBuf_GetError(&(pMe->OutBuf))) {
       // Items didn't fit in the buffer.
       // This check catches this condition for all the appends and inserts
       // so checks aren't needed when the appends and inserts are performed.
       // And of course UsefulBuf will never overrun the input buffer given
       // to it. No complex analysis of the error handling in this file is
       // needed to know that is true. Just read the UsefulBuf code.
-      pCtx->uError = QCBOR_ERR_BUFFER_TOO_SMALL;
+      pMe->uError = QCBOR_ERR_BUFFER_TOO_SMALL;
       // QCBOR_ERR_BUFFER_TOO_SMALL masks other errors, but that is
       // OK. Once the caller fixes this, they'll be unmasked.
    }
 
-   return (QCBORError)pCtx->uError;
+   return (QCBORError)pMe->uError;
 }
 
 
-/* ===========================================================================
- END OF PRIVATE INLINE IMPLEMENTATION
-
- =========================================================================== */
+/* ========================================================================
+     END OF PRIVATE INLINE IMPLEMENTATION
+   ======================================================================== */
 
 #ifdef __cplusplus
 }
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index a7cb440..a72cfdd 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -57,13 +57,24 @@
 
 
 /* The largest offset to the start of an array or map. It is slightly
- less than UINT32_MAX so the error condition can be tests on 32-bit machines.
+ less than UINT32_MAX so the error condition can be tested on 32-bit machines.
  UINT32_MAX comes from uStart in QCBORTrackNesting being a uin32_t.
 
  This will cause trouble on a machine where size_t is less than 32-bits.
  */
 #define QCBOR_MAX_ARRAY_OFFSET  (UINT32_MAX - 100)
 
+
+/* The number of tags that are 16-bit or larger that can be handled
+ in a decode.
+ */
+#define QCBOR_NUM_MAPPED_TAGS 4
+
+/* The number of tags (of any size) recorded for an individual item. */
+#define QCBOR_MAX_TAGS_PER_ITEM1 4
+
+
+
 /*
  PRIVATE DATA STRUCTURE
 
@@ -113,21 +124,89 @@
 /*
  PRIVATE DATA STRUCTURE
 
- Holds the data for array and map nesting for decoding work. This structure
- and the DecodeNesting_xxx functions form an "object" that does the work
- for arrays and maps.
+ Holds the data for array and map nesting for decoding work. This
+ structure and the DecodeNesting_Xxx() functions in qcbor_decode.c
+ form an "object" that does the work for arrays and maps. All access
+ to this structure is through DecodeNesting_Xxx() functions.
 
- Size approximation (varies with CPU/compiler):
-   64-bit machine: 4 * 16 + 8 = 72
-   32-bit machine: 4 * 16 + 4 = 68
+ 64-bit machine size
+   128 = 16 * 8 for the two unions
+   64  = 16 * 4 for the uLevelType, 1 byte padded to 4 bytes for alignment
+   16  = 16 bytes for two pointers
+   208 TOTAL
+
+ 32-bit machine size is 200 bytes
  */
 typedef struct __QCBORDecodeNesting  {
-  // PRIVATE DATA STRUCTURE
-   struct {
-      uint16_t uCount;
-      uint8_t  uMajorType;
-   } pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING1+1],
-   *pCurrent;
+   // PRIVATE DATA STRUCTURE
+   struct nesting_decode_level {
+      /*
+       This keeps tracking info for each nesting level. There are two
+       main types of levels:
+         1) Byte count tracking. This is for the top level input CBOR
+         which might be a single item or a CBOR sequence and byte
+         string wrapped encoded CBOR.
+         2) Item tracking. This is for maps and arrays.
+
+       uLevelType has value QCBOR_TYPE_BYTE_STRING for 1) and
+       QCBOR_TYPE_MAP or QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP_AS_ARRAY
+       for 2).
+
+       Item tracking is either be for definite or indefinite length
+       maps/arrays. For definite lengths, the total count and items
+       unconsumed are tracked. For indefinite length, uTotalCount is
+       QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH (UINT16_MAX) and there
+       is no per-item count of members. For indefinite length maps and
+       arrays, uCountCursor is UINT16_MAX if not consumed and zero if
+       it is consumed in the pre-order traversal. Additionally, if
+       entered in bounded mode, uCountCursor is
+       QCBOR_COUNT_INDICATES_ZERO_LENGTH to indicate it is empty.
+
+       This also records whether a level is bounded or not.  All
+       byte-count tracked levels (the top-level sequence and
+       bstr-wrapped CBOR) are bounded. Maps and arrays may or may not
+       be bounded. They are bounded if they were Entered() and not if
+       they were traversed with GetNext(). They are marked as bounded
+       by uStartOffset not being UINT32_MAX.
+       */
+      /*
+       If uLevelType can put in a separately indexed array, the union/
+       struct will be 8 bytes rather than 9 and a lot of wasted
+       padding for alignment will be saved.
+       */
+      uint8_t  uLevelType;
+      union {
+         struct {
+#define QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH UINT16_MAX
+#define QCBOR_COUNT_INDICATES_ZERO_LENGTH UINT16_MAX-1
+            uint16_t uCountTotal;
+            uint16_t uCountCursor;
+#define QCBOR_NON_BOUNDED_OFFSET UINT32_MAX
+            uint32_t uStartOffset;
+         } ma; /* for maps and arrays */
+         struct {
+            uint32_t uEndOfBstr;
+            uint32_t uPreviousEndOffset;
+         } bs; /* for top-level sequence and bstr wrapped CBOR */
+      } u;
+   } pLevels[QCBOR_MAX_ARRAY_NESTING1+1],
+    *pCurrent,
+    *pCurrentBounded;
+   /*
+    pCurrent is for item-by-item pre-order traversal.
+
+    pCurrentBounded points to the current bounding level or is NULL if
+    there isn't one.
+
+    pCurrent must always be below pCurrentBounded as the pre-order
+    traversal is always bounded by the bounding level.
+
+    When a bounded level is entered, the pre-order traversal is set to
+    the first item in the bounded level. When a bounded level is
+    exited, the pre-order traversl is set to the next item after the
+    map, array or bstr. This may be more than one level up, or even
+    the end of the input CBOR.
+    */
 } QCBORDecodeNesting;
 
 
@@ -152,11 +231,10 @@
    // PRIVATE DATA STRUCTURE
    UsefulInputBuf InBuf;
 
-   uint8_t        uDecodeMode;
-   uint8_t        bStringAllocateAll;
 
    QCBORDecodeNesting nesting;
 
+
    // If a string allocator is configured for indefinite-length
    // strings, it is configured here.
    QCORInternalAllocator StringAllocator;
@@ -168,9 +246,19 @@
    uint32_t uMemPoolSize;
    uint32_t uMemPoolFreeOffset;
 
-   // This is NULL or points to QCBORTagList.
-   // It is type void for the same reason as above.
-   const void *pCallerConfiguredTagList;
+   // A cached offset to the end of the current map
+   // 0 if no value is cached.
+#define QCBOR_MAP_OFFSET_CACHE_INVALID UINT32_MAX
+   uint32_t uMapEndOffsetCache;
+
+   uint8_t  uDecodeMode;
+   uint8_t  bStringAllocateAll;
+   uint8_t  uLastError;  // QCBORError stuffed into a uint8_t
+
+   uint64_t auMappedTags[QCBOR_NUM_MAPPED_TAGS];
+
+   uint16_t uLastTags[QCBOR_MAX_TAGS_PER_ITEM1];
+
 };
 
 // Used internally in the impementation here
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
new file mode 100644
index 0000000..df0067b
--- /dev/null
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -0,0 +1,2342 @@
+/*============================================================================
+  qcbor_spiffy_decode.h -- higher-level easier-to-use CBOR decoding.
+
+  Copyright (c) 2020, Laurence Lundblade. All rights reserved.
+
+  SPDX-License-Identifier: BSD-3-Clause
+
+  See BSD-3-Clause license in README.md
+
+  Forked from qcbor_decode.h on 7/23/2020
+  ============================================================================*/
+#ifndef qcbor_spiffy_decode_h
+#define qcbor_spiffy_decode_h
+
+
+#include "qcbor/qcbor_decode.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} // Keep editor indention formatting happy
+#endif
+#endif
+
+
+/**
+ @file qcbor_spiffy_decode.h
+
+ @anchor SpiffyDecode
+ # Spiffy Decode
+
+ This section just discusses spiff decoding assuming familiarity with
+ the general description of this encoder / decoder in section
+ @ref Overview and @ref BasicDecoding.
+
+ Spiffy decode is extra decode features over and above the @ref
+ BasicDecode features that generally are easier to use, mirror the
+ encoding functions better and can result in smaller code size for
+ larger and more complex CBOR protocols.  In particular, spiffy decode
+ facilitates getting the next data item of a specific type, setting an
+ error if it is not of that type. It facilitates explicitly entering
+ and exiting arrays and maps. It facilates fetching items by label
+ from a map including duplicate label detection.
+
+ Encoded CBOR can be viewed to have a tree structure where the leaf
+ nodes are non-aggregate types like integers and strings and the
+ intermediate nodes are either arrays or maps. Fundamentally, all
+ decoding is a pre-order traversal of the tree. Calling
+ QCBORDecode_GetNext() repeatedly will perform this.
+
+ This pre-order traversal gives natural decoding of arrays where the
+ array members are taken in order, but does not give natural decoding
+ of maps where access by label is usually preferred.  Using the
+ QCBORDecode_EnterMap() and QCBORDecode_GetXxxxInMapX() methods like
+ QCBORDecode_GetInt64InMapN(), map items can be accessed by
+ label. QCBORDecode_EnterMap() bounds decoding to a particular
+ map. QCBORDecode_GetXxxxInMapX() methods allows decoding the item of
+ a particular label in the particular map. This can be used with
+ nested maps by using QCBORDecode_EnterMapFromMapX().
+
+ When QCBORDecode_EnterMap() is called, pre-order traversal continues
+ to work. There is a cursor that is run over the tree with calls to
+ QCBORDecode_GetNext(). This can be intermixed with calls to
+ QCBORDecode_GetXxxxInMapX(). The pre-order traversal is limited just
+ to the map entered. Attempts to QCBORDecode_GetNext() beyond the end
+ of the map will give the @ref QCBOR_ERR_NO_MORE_ITEMS error.
+
+ There is also QCBORDecode_EnterArray() to decode arrays. It will
+ narrow the traversal to the extent of the array entered.
+
+ All the QCBORDecode_GetXxxxInMapX() methods support duplicate label
+ detection and will result in an error if the map has duplicate
+ labels.
+
+ All the QCBORDecode_GetXxxxInMapX() methods are implemented by
+ performing the pre-order traversal of the map to find the labeled
+ item everytime it is called. It doesn't build up a hash table, a
+ binary search tree or some other efficiently searchable structure
+ internally. For simple trees this is fine and for high-speed CPUs
+ this is fine, but for complex trees on slow CPUs, it may have
+ performance issues (these have not be quantified yet). One way ease
+ this is to use QCBORDecode_GetItemsInMap() which allows decoding of a
+ list of items expected in an map in one traveral.
+
+ @anchor Decode-Errors
+ ## Spiffy Decode Errors
+
+ Like encoding, decoding maintains an internal error state. Once a
+ call to the decoder returns an error, this error state is entered and
+ subsequent decoder calls do nothing. This allows for prettier and
+ cleaner decoding code. In some cases the only error check that may be
+ necessary is the return code from QCBORDecode_Finish().
+
+ The internal error can be retrived with QCBORDecode_GetError(). Any
+ further attempts to get specific data types will do nothing so it is
+ safe for code to get many items without checking the error on each
+ one as long as there is an error check before any data is used.  The
+ error state is reset only by re initializing the decoder or
+ QCBORDecode_GetErrorAndReset().  QCBORDecode_GetErrorAndReset() is
+ mainly useful after a failure to get an item in a map by label.
+
+ An easy and clean way to use this decoder is to always use EnterMap
+ and EnterArray for each array or map. They will error if the input
+ CBOR is not the expected array or map.  Then use GetInt, GetString to
+ get the individual items of of the maps and arrays making use of the
+ internal error tracking provided by this decoder. The only error
+ check needed is the call to Finish.
+
+ QCBORDecode_GetNext() is the exception to this. It returns an
+ error. It doesn't set the internal error state. It will attempt to
+ decode evening when in the error state.
+
+ When getting an item by label from a map the whole map is traversed
+ including traversal of nested arrays and maps. If there is any
+ unrecoverable error anywhere in the that traversal the retrieval by
+ label will fail and the unrecoverable error will be returned even if
+ it is not due to the labeled item being sought. Recoverable errors
+ will be ignored unless they are on the item being sought, in which
+ case the unrecoverable error will be returned. Unrecoverable errors
+ are those indicated by QCBORDecode_IsUnrecoverableError().
+
+ @anchor Tag-Usage
+ ## Tag Usage
+
+ Data types beyond the basic CBOR types of numbers, strings, maps and
+ arrays are called tags. The main registry of these new types is in in
+ the IANA CBOR tags registry. These new types may be simple such a
+ number that is to be interpreted as a date, or of moderate complexity
+ such as defining a decimal fraction that is an array containing a
+ mantissa and exponent, or complex such as format for signing and
+ encryption.
+
+ When a tag occurs in a protocol it is encoded as an integer tag
+ number plus the content of the tag.
+
+ The content format of a tag may also be "borrowed". For example, a
+ protocol definition may say that a particular data item is an epoch
+ date just like tag 1, but not actually tag 1. In practice the
+ difference is the presence or absence of the integer tag number in
+ the encoded CBOR.
+
+ The decoding functions for these new types takes a tag requirement
+ parameter to say whether the item is a tag, is just borrowing the
+ content format and is not a tag, or whether either is OK.
+
+ If the parameter indicates the item must be a tag (@ref
+ QCBOR_TAG_REQUIREMENT_TAG), then @ref QCBOR_ERR_UNEXPECTED_TYPE is
+ set if it is not one of the expected tag types. To decode correctly
+ the contents of the tag must also be of the correct type. For
+ example, to decode an epoch date tag the content must be an integer
+ or floating-point value.
+
+ If the parameter indicates it should not be a tag 
+ (@ref  QCBOR_TAG_REQUIREMENT_NOT_A_TAG), then
+  @ref QCBOR_ERR_UNEXPECTED_TYPE set if it is a tag or the type of the
+ encoded CBOR is not what is expected.  In the example of an epoch
+ date, the data type must be an integer or floating-point value. This
+ is the case where the content format of a tag is borrowed.
+
+ The parameter can also indicate that either a tag or no tag is
+ allowed ( @ref QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG ).  A good protocol
+ design should however be clear and choose one or the other and not
+ need this option. This is a way to implement "be liberal in what you
+ accept", however these days that is less in favor. See
+ https://tools.ietf.org/id/draft-thomson-postel-was-wrong-03.html.
+
+ Map searching works with indefinite length strings. A string
+ allocator must be set up the same as for any handling of indefinite
+ length strings.  However, It currently over-allocates memory from the
+ string pool and thus requires a much larger string pool than it
+ should. The over-allocation happens every time a map is searched by
+ label.  (This may be corrected in the future).
+*/
+
+
+
+
+/** The data item must be a tag of the expected type. It is an error
+ if it is not. For example when calling QCBORDecode_GetEpochDate(),
+ the data item must be an @ref CBOR_TAG_DATE_EPOCH tag.
+ See @ref Tag-Usage. */
+#define QCBOR_TAG_REQUIREMENT_TAG 0
+
+/** The data item must be of the type expected for content data type
+ being fetched. It is an error if it is not. For example, when
+ calling QCBORDecode_GetEpochDate() and it must not be an @ref
+ CBOR_TAG_DATE_EPOCH tag. See @ref Tag-Usage. */
+#define QCBOR_TAG_REQUIREMENT_NOT_A_TAG  1
+
+/** Either of the above two are allowed. This allows implementation of
+ being liberal in what you receive, but it is better if CBOR-based
+ protocols pick one and stick to and not required the reciever to
+ take either. See @ref Tag-Usage. */
+#define QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG 2
+
+/** Add this into the above value if other tags not processed by QCBOR
+ are to be allowed to surround the data item. See @ref Tag-Usage. */
+#define QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS 0x80
+
+
+
+
+/** Conversion will proceed if the CBOR item to be decoded is an
+    integer or either type 0 (unsigned) or type 1 (negative). */
+#define QCBOR_CONVERT_TYPE_XINT64           0x01
+/** Conversion will proceed if the CBOR item to be decoded is either
+    double, single or half-precision floating-point (major type 7). */
+#define QCBOR_CONVERT_TYPE_FLOAT            0x02
+/** Conversion will proceed if the CBOR item to be decoded is a big
+    number, positive or negative (tag 2 or tag 3). */
+#define QCBOR_CONVERT_TYPE_BIG_NUM          0x04
+/** Conversion will proceed if the CBOR item to be decoded is a
+    decimal fraction (tag 4). */
+#define QCBOR_CONVERT_TYPE_DECIMAL_FRACTION 0x08
+/** Conversion will proceed if the CBOR item to be decoded is a big
+    float (tag 5). */
+#define QCBOR_CONVERT_TYPE_BIGFLOAT         0x10
+
+
+
+
+/**
+ @brief Decode next item into a signed 64-bit integer.
+
+ @param[in] pCtx      The decode context.
+ @param[out] pnValue  The returned 64-bit signed integer.
+
+ The CBOR data item to decode must be a positive or negative integer
+ (CBOR major type 0 or 1). If not @ref QCBOR_ERR_UNEXPECTED_TYPE is set.
+
+ If the CBOR integer is either too large or too small to fit in an
+ int64_t, the error @ref QCBOR_ERR_INT_OVERFLOW or @ref
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW is set.  Note that type 0
+ unsigned integers can be larger than will fit in an int64_t and type
+ 1 negative integers can be smaller than will fit in an int64_t.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See also QCBORDecode_GetUInt64(), QCBORDecode_GetInt64Convert() and
+ QCBORDecode_GetInt64ConvertAll().
+ */
+static void QCBORDecode_GetInt64(QCBORDecodeContext *pCtx,
+                                 int64_t            *pnValue);
+
+static void QCBORDecode_GetInt64InMapN(QCBORDecodeContext *pCtx,
+                                       int64_t             nLabel,
+                                       int64_t            *pnValue);
+
+static void QCBORDecode_GetInt64InMapSZ(QCBORDecodeContext *pCtx,
+                                        const char         *szLabel,
+                                        int64_t            *pnValue);
+
+
+/**
+ @brief Decode next item into a signed 64-bit integer with basic conversions.
+
+ @param[in] pCtx   The decode context.
+ @param[in] uConvertTypes The integer conversion options.
+ @param[out] pnValue  The returned 64-bit signed integer.
+
+ @c uConvertTypes controls what conversions this will perform and thus
+ what CBOR types will be decoded.  @c uConvertType is a bit map
+ listing the conversions to be allowed. This function supports @ref
+ QCBOR_CONVERT_TYPE_XINT64 and @ref QCBOR_CONVERT_TYPE_FLOAT
+ conversions.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ If the CBOR data type can never be convered by this function or the
+ conversion was not selected in @c uConversionTypes @ref
+ @ref QCBOR_ERR_UNEXPECTED_TYPE is set.
+
+ When converting floating-point values, the integer is rounded to the
+ nearest integer using llround(). By default, floating-point suport is
+ enabled for QCBOR.
+
+ If floating-point HW use is disabled this will set
+ @ref QCBOR_ERR_HW_FLOAT_DISABLED if a single-precision
+ number is encountered. If half-precision support is disabled,
+ this will set QCBOR_ERR_HALF_PRECISION_DISABLED if
+ a half-precision number is encountered.
+
+ See also QCBORDecode_GetInt64ConvertAll() which will perform the same
+ conversions as this and a lot more at the cost of adding more object
+ code to your executable.
+ */
+static void QCBORDecode_GetInt64Convert(QCBORDecodeContext *pCtx,
+                                        uint32_t            uConvertTypes,
+                                        int64_t            *pnValue);
+
+static void QCBORDecode_GetInt64ConvertInMapN(QCBORDecodeContext *pCtx,
+                                              int64_t             nLabel,
+                                              uint32_t            uConvertTypes,
+                                              int64_t            *pnValue);
+
+static void QCBORDecode_GetInt64ConvertInMapSZ(QCBORDecodeContext *pCtx,
+                                               const char         *szLabel,
+                                               uint32_t            uConvertTypes,
+                                               int64_t            *pnValue);
+
+
+/**
+ @brief Decode next item into a signed 64-bit integer with conversions.
+
+ @param[in] pCtx   The decode context.
+ @param[in] uConvertTypes The integer conversion options.
+ @param[out] pnValue  The returned 64-bit signed integer.
+
+ This is the same as QCBORDecode_GetInt64Convert() but additionally
+ supports conversion from positive and negative bignums, decimal
+ fractions and big floats, including decimal fractions and big floats
+ that use bignums. The conversion types supported are @ref
+ QCBOR_CONVERT_TYPE_XINT64, @ref QCBOR_CONVERT_TYPE_FLOAT, @ref
+ QCBOR_CONVERT_TYPE_BIG_NUM, @ref QCBOR_CONVERT_TYPE_DECIMAL_FRACTION
+ and @ref QCBOR_CONVERT_TYPE_BIGFLOAT.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ Note that most these types can support numbers much larger that can
+ be represented by in a 64-bit integer, so @ref
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW may often be encountered.
+
+ When converting bignums and decimal fractions @ref
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW will be set if the result is
+ below 1, unless the mantissa is zero, in which case the coversion is
+ successful and the value of 0 is returned.
+
+ See also QCBORDecode_GetInt64ConvertAll() which does some of these
+ conversions, but links in much less object code. See also
+ QCBORDecode_GetUInt64ConvertAll().
+ */
+void QCBORDecode_GetInt64ConvertAll(QCBORDecodeContext *pCtx,
+                                    uint32_t            uConvertTypes,
+                                    int64_t            *pnValue);
+
+void QCBORDecode_GetInt64ConvertAllInMapN(QCBORDecodeContext *pCtx,
+                                          int64_t             nLabel,
+                                          uint32_t            uConvertTypes,
+                                          int64_t            *pnValue);
+
+void QCBORDecode_GetInt64ConvertAllInMapSZ(QCBORDecodeContext *pCtx,
+                                           const char         *szLabel,
+                                           uint32_t            uConvertTypes,
+                                           int64_t            *pnValue);
+
+
+/**
+ @brief Decode next item into an unsigned 64-bit integer.
+
+ @param[in] pCtx   The decode context.
+ @param[out] puValue  The returned 64-bit unsigned integer.
+
+ This is the same as QCBORDecode_GetInt64(), but returns an unsigned integer
+ and thus can only decode CBOR positive integers.
+ @ref QCBOR_ERR_NUMBER_SIGN_CONVERSION is set if the input is a negative
+ integer.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See also QCBORDecode_GetUInt64Convert() and QCBORDecode_GetUInt64ConvertAll().
+*/
+static void QCBORDecode_GetUInt64(QCBORDecodeContext *pCtx,
+                                  uint64_t           *puValue);
+
+static void QCBORDecode_GetUInt64InMapN(QCBORDecodeContext *pCtx,
+                                        int64_t             nLabel,
+                                        uint64_t           *puValue);
+
+static void QCBORDecode_GetUInt64InMapSZ(QCBORDecodeContext *pCtx,
+                                         const char         *szLabel,
+                                         uint64_t           *puValue);
+
+
+/**
+ @brief Decode next item as an unsigned 64-bit integer with basic conversions.
+
+ @param[in] pCtx   The decode context.
+ @param[in] uConvertTypes The integer conversion options.
+ @param[out] puValue  The returned 64-bit unsigned integer.
+
+ This is the same as QCBORDecode_GetInt64Convert(), but returns an
+ unsigned integer and thus sets @ref QCBOR_ERR_NUMBER_SIGN_CONVERSION
+ is set if the value to be decoded is negatve.
+
+ If floating-point HW use is disabled this will set
+ @ref QCBOR_ERR_HW_FLOAT_DISABLED if a single-precision
+ number is encountered. If half-precision support is disabled,
+ this will set QCBOR_ERR_HALF_PRECISION_DISABLED if
+ a half-precision number is encountered.
+
+ See also QCBORDecode_GetUInt64Convert() and
+ QCBORDecode_GetUInt64ConvertAll().
+*/
+static void QCBORDecode_GetUInt64Convert(QCBORDecodeContext *pCtx,
+                                         uint32_t            uConvertTypes,
+                                         uint64_t           *puValue);
+
+static void QCBORDecode_GetUInt64ConvertInMapN(QCBORDecodeContext *pCtx,
+                                               int64_t             nLabel,
+                                               uint32_t            uConvertTypes,
+                                               uint64_t           *puValue);
+
+static void QCBORDecode_GetUInt64ConvertInMapSZ(QCBORDecodeContext *pCtx,
+                                                const char         *szLabel,
+                                                uint32_t            uConvertTypes,
+                                                uint64_t           *puValue);
+
+
+/**
+ @brief Decode next item into an unsigned 64-bit integer with conversions
+
+ @param[in] pCtx           The decode context.
+ @param[in] uConvertTypes  The integer conversion options.
+ @param[out] puValue       The returned 64-bit unsigned integer.
+
+ This is the same as QCBORDecode_GetInt64ConvertAll(), but returns an
+ unsigned integer and thus sets @ref QCBOR_ERR_NUMBER_SIGN_CONVERSION
+ if the value to be decoded is negatve.
+
+ See also QCBORDecode_GetUInt64() and
+ QCBORDecode_GetUInt64Convert().
+*/
+void QCBORDecode_GetUInt64ConvertAll(QCBORDecodeContext *pCtx,
+                                     uint32_t            uConvertTypes,
+                                     uint64_t           *puValue);
+
+void QCBORDecode_GetUInt64ConvertAllInMapN(QCBORDecodeContext *pCtx,
+                                           int64_t             nLabel,
+                                           uint32_t            uConvertTypes,
+                                           uint64_t           *puValue);
+
+void QCBORDecode_GetUInt64ConvertAllInMapSZ(QCBORDecodeContext *pCtx,
+                                            const char         *szLabel,
+                                            uint32_t            uConvertTypes,
+                                            uint64_t           *puValue);
+
+
+
+
+/**
+ @brief Decode the next item as a byte string
+
+ @param[in] pCtx   The decode context
+ @param[out] pBytes  The decoded byte string
+
+ The CBOR item to decode must be a byte string, CBOR type 2.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ If the CBOR tem to decode is not a byte string, the @ref
+ QCBOR_ERR_UNEXPECTED_TYPE error is set.
+ */
+static void QCBORDecode_GetByteString(QCBORDecodeContext *pCtx,
+                                      UsefulBufC         *pBytes);
+
+static void QCBORDecode_GetByteStringInMapN(QCBORDecodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            UsefulBufC         *pBytes);
+
+static void QCBORDecode_GetByteStringInMapSZ(QCBORDecodeContext *pCtx,
+                                             const char         *szLabel,
+                                             UsefulBufC         *pBytes);
+
+
+/**
+ @brief Decode the next item as a text string.
+
+ @param[in] pCtx   The decode context.
+ @param[out] pText  The decoded byte string.
+
+ The CBOR item to decode must be a text string, CBOR type 3.
+
+ See @ref Decode-Errors for discussion on how error handling works.  It the CBOR item
+ to decode is not a text string, the @ref QCBOR_ERR_UNEXPECTED_TYPE
+ error is set.
+
+ This does no translation of line endings. See QCBOREncode_AddText()
+ for a discussion of line endings in CBOR.
+*/
+static void QCBORDecode_GetTextString(QCBORDecodeContext *pCtx,
+                                      UsefulBufC         *pText);
+
+static void QCBORDecode_GetTextStringInMapN(QCBORDecodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            UsefulBufC         *pText);
+
+static void QCBORDecode_GetTextStringInMapSZ(QCBORDecodeContext *pCtx,
+                                             const char         *szLabel,
+                                             UsefulBufC         *pText);
+
+
+
+
+/**
+ @brief Decode next item into a double floating-point value.
+
+ @param[in] pCtx   The decode context
+ @param[out] pValue  The returned floating-point value.
+
+ The CBOR data item to decode must be a half-precision,
+ single-precision or double-precision floating-point value.  If not
+ @ref QCBOR_ERR_UNEXPECTED_TYPE is set.
+
+ If floating-point HW use is disabled this will set
+ @ref QCBOR_ERR_HW_FLOAT_DISABLED if a single-precision
+ number is encountered. If half-precision support is disabled,
+ this will set QCBOR_ERR_HALF_PRECISION_DISABLED if
+ a half-precision number is encountered.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See also QCBORDecode_GetDoubleConvert() and
+ QCBORDecode_GetDoubleConvertAll().
+*/
+static void QCBORDecode_GetDouble(QCBORDecodeContext *pCtx,
+                                  double             *pValue);
+
+static void QCBORDecode_GetDoubleInMapN(QCBORDecodeContext *pCtx,
+                                        int64_t             nLabel,
+                                        double             *pdValue);
+
+static void QCBORDecode_GetDoubleInMapSZ(QCBORDecodeContext *pCtx,
+                                         const char         *szLabel,
+                                         double             *pdValue);
+
+
+/**
+ @brief Decode next item into a double floating-point value with basic conversion.
+
+ @param[in] pCtx   The decode context.
+ @param[in] uConvertTypes The integer conversion options.
+ @param[out] pdValue  The returned floating-point value.
+
+ This will decode CBOR integer and floating-point numbers, returning
+ them as a double floating-point number. This function supports @ref
+ QCBOR_CONVERT_TYPE_XINT64 and @ref QCBOR_CONVERT_TYPE_FLOAT
+ conversions. If the encoded CBOR is not one of the requested types or a type
+ not supported by this function, @ref QCBOR_ERR_UNEXPECTED_TYPE is
+ set.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ If floating-point HW use is disabled this will set
+ @ref QCBOR_ERR_HW_FLOAT_DISABLED if a single-precision
+ number is encountered. If half-precision support is disabled,
+ this will set QCBOR_ERR_HALF_PRECISION_DISABLED if
+ a half-precision number is encountered.
+
+ Positive and negative integers can always be converted to
+ floating-point, so this will never error on CBOR major type 0 or 1.
+
+ Note that a large 64-bit integer can have more precision (64 bits)
+ than even a double floating-point (52 bits) value, so there is loss
+ of precision in some conversions.
+
+ See also QCBORDecode_GetDouble() and QCBORDecode_GetDoubleConvertAll().
+*/
+static void QCBORDecode_GetDoubleConvert(QCBORDecodeContext *pCtx,
+                                         uint32_t            uConvertTypes,
+                                         double             *pdValue);
+
+static void QCBORDecode_GetDoubleConvertInMapN(QCBORDecodeContext *pCtx,
+                                               int64_t             nLabel,
+                                               uint32_t            uConvertTypes,
+                                               double             *pdValue);
+
+static void QCBORDecode_GetDoubleConvertInMapSZ(QCBORDecodeContext *pCtx,
+                                                const char         *szLabel,
+                                                uint32_t            uConvertTypes,
+                                                double             *pdValue);
+
+
+/**
+ @brief Decode next item as a double floating-point value with conversion.
+
+ @param[in] pCtx           The decode context.
+ @param[in] uConvertTypes  The integer conversion options.
+ @param[out] pdValue       The returned floating-point value.
+
+ This is the same as QCBORDecode_GetDoubleConvert() but supports many
+ more conversions at the cost of linking in more object code. The
+ conversion types supported are @ref QCBOR_CONVERT_TYPE_XINT64, @ref
+ QCBOR_CONVERT_TYPE_FLOAT, @ref QCBOR_CONVERT_TYPE_BIG_NUM, @ref
+ QCBOR_CONVERT_TYPE_DECIMAL_FRACTION and @ref
+ QCBOR_CONVERT_TYPE_BIGFLOAT.
+
+ Big numbers, decimal fractions and big floats that are too small or
+ too large to be reprented as a double floating-point number will be
+ returned as plus or minus zero or infinity rather than setting an
+ under or overflow error.
+
+ There is often loss of precision in the conversion.
+
+ See also QCBORDecode_GetDoubleConvert() and QCBORDecode_GetDoubleConvert().
+*/
+void QCBORDecode_GetDoubleConvertAll(QCBORDecodeContext *pCtx,
+                                     uint32_t            uConvertTypes,
+                                     double             *pdValue);
+
+void QCBORDecode_GetDoubleConvertAllInMapN(QCBORDecodeContext *pCtx,
+                                           int64_t             nLabel,
+                                           uint32_t            uConvertTypes,
+                                           double             *pdValue);
+
+void QCBORDecode_GetDoubleConvertAllInMapSZ(QCBORDecodeContext *pCtx,
+                                            const char         *szLabel,
+                                            uint32_t            uConvertTypes,
+                                            double             *pdValue);
+
+
+
+
+/**
+ @brief Enter an array for decoding in bounded mode.
+
+ @param[in] pCtx    The decode context.
+ @param[out] pItem  The optionally returned QCBORItem that has the
+                    label and tags for the array. May be @c NULL (and
+                    usually is).
+
+ This enters an array for decoding in bounded mode. The items in array
+ are decoded in order the same as when not in bounded mode, but the
+ decoding will not proceed past the end or the array. The error @ref
+ QCBOR_ERR_NO_MORE_ITEMS will be set when the end of the array is
+ encountered. To decode past the end of the array,
+ QCBORDecode_ExitArray() must be called. Also, QCBORDecode_Finish()
+ will return an error if all arrays that were enetered are not exited.
+
+ This works the same for definite and indefinite length arrays.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ If attempting to enter a data item that is not an array @ref
+ QCBOR_ERR_UNEXPECTED_TYPE wil be set.
+
+ Nested arrays and maps may be entered to a depth of @ref
+ QCBOR_MAX_ARRAY_NESTING.
+
+ See also QCBORDecode_ExitArray(), QCBORDecode_EnterMap() and
+ QCBORDecode_EnterBstrWrapped().
+*/
+static void QCBORDecode_EnterArray(QCBORDecodeContext *pCtx, QCBORItem *pItem);
+
+void QCBORDecode_EnterArrayFromMapN(QCBORDecodeContext *pMe, int64_t uLabel);
+
+void QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel);
+
+
+/**
+ @brief Exit an array that has been enetered.
+
+ @param[in] pCtx   The decode context.
+
+ An array must have been entered for this to succeed.
+
+ The items in the array that was entered do not have to have been
+ consumed for this to succeed.
+
+ This sets the pre-order traversal cursor to the item after the array
+ that was exited.
+*/
+static void QCBORDecode_ExitArray(QCBORDecodeContext *pCtx);
+
+
+
+
+/**
+ @brief Enter a map for decoding and searching.
+
+ @param[in] pCtx    The decode context.
+ @param[out] pItem  The optionally returned QCBORItem that has the
+                    label and tags for the map. May be @c NULL (and
+                    usually is).
+
+ The next item in the CBOR input must be map or this sets an error.
+
+ This puts the decoder in bounded mode which narrows decoding to the
+ map entered and enables getting items by label.
+
+ All items in the map must be well-formed to be able to search it by
+ label because a full traversal is done for each search. If not, the
+ search will retun an error for the item that is not well-formed.
+ This will be the first non-well-formed item which may not be the item
+ with the label that is the target of the search.
+
+ Nested maps can be decoded like this by entering each map in turn.
+
+ Call QCBORDecode_ExitMap() to exit the current map decoding
+ level. When all map decoding layers are exited then bounded mode is
+ fully exited.
+
+ While in bounded mode, QCBORDecode_GetNext() works as usual on the
+ map and the in-order traversal cursor is maintained. It starts out at
+ the first item in the map just entered. Attempts to get items off the
+ end of the map will give error @ref QCBOR_ERR_NO_MORE_ITEMS rather
+ going to the next item after the map as it would when not in bounded
+ mode.
+
+ Exiting leaves the pre-order cursor at the data item following the
+ last entry in the map or at the end of the input CBOR if there
+ nothing after the map.
+
+ Entering and Exiting a map is a way to skip over an entire map and
+ its contents. After QCBORDecode_ExitMap(), the pre-order traversal
+ cursor will be at the first item after the map.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See also QCBORDecode_EnterArray() and QCBORDecode_EnterBstrWrapped().
+ Entering and exiting any nested combination of maps, arrays and
+ bstr-wrapped CBOR is supported up to the maximum of @ref
+ QCBOR_MAX_ARRAY_NESTING.
+ */
+static void QCBORDecode_EnterMap(QCBORDecodeContext *pCtx, QCBORItem *pItem);
+
+void QCBORDecode_EnterMapFromMapN(QCBORDecodeContext *pCtx, int64_t nLabel);
+
+void QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pCtx, const char *szLabel);
+
+
+/**
+ @brief Exit a map that has been enetered.
+
+ @param[in] pCtx   The decode context.
+
+ A map must have been entered for this to succeed.
+
+ The items in the map that was entered do not have to have been
+ consumed for this to succeed.
+
+ This sets thepre-order traversal cursor to the item after
+ the map that was exited.
+*/
+static void QCBORDecode_ExitMap(QCBORDecodeContext *pCtx);
+
+
+/**
+ @brief Get an item in map by label and type.
+
+ @param[in] pCtx   The decode context.
+ @param[in] nLabel The integer label.
+ @param[in] uQcborType  The QCBOR type. One of @c QCBOR_TYPE_XXX.
+ @param[out] pItem  The returned item.
+
+ A map must have been entered to use this. If not @ref QCBOR_ERR_MAP_NOT_ENTERED is
+ set.
+
+ The map is searched for an item of the requested label and type.
+ @ref QCBOR_TYPE_ANY can be given to search for the label without
+ matching the type.
+
+ This will always search the entire map. This will always perform
+ duplicate label detection, setting @ref QCBOR_ERR_DUPLICATE_LABEL if
+ there is more than one occurance of the label being searched for.
+
+ Duplicate label detection is performed for the item being sought, but
+ only for the item being sought.
+
+ This performs a full decode of every item in the map being searched,
+ which involves a full traversal of every item. For maps with little
+ nesting, this is of little consequence, but may be of consequence for
+ large deeply nested CBOR structures on slow CPUs.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See also QCBORDecode_GetItemsInMap() for error discussion.
+*/
+void QCBORDecode_GetItemInMapN(QCBORDecodeContext *pCtx,
+                               int64_t             nLabel,
+                               uint8_t             uQcborType,
+                               QCBORItem          *pItem);
+
+void QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
+                                const char         *szLabel,
+                                uint8_t             uQcborType,
+                                QCBORItem          *pItem);
+
+
+/**
+ @brief Get a group of labeled items all at once from a map
+
+ @param[in] pCtx   The decode context.
+ @param[in,out] pItemList  On input the items to search for. On output the returned items.
+
+ This gets several labeled items out of a map.
+
+ @c pItemList is an array of items terminated by an item with @c
+ uLabelType @ref QCBOR_TYPE_NONE.
+
+ On input the labels to search for are in the @c uLabelType and label
+ fields in the items in @c pItemList.
+
+ Also on input are the requested QCBOR types in the field @c
+ uDataType.  To match any type, searching just by label, @c uDataType
+ can be @ref QCBOR_TYPE_ANY.
+
+ This is a CPU-efficient way to decode a bunch of items in a map. It
+ is more efficient than scanning each individually because the map
+ only needs to be traversed once.
+
+ This will return maps and arrays that are in the map, but provides no
+ way to descend into and decode them. Use
+ QCBORDecode_EnterMapinMapN(), QCBORDecode_EnterArrayInMapN() and such
+ to descend into and process maps and arrays.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ The following errors are set:
+
+ @ref QCBOR_ERR_MAP_NOT_ENTERED when calling this without previousl
+ calling QCBORDecode_EnterMap() or other methods to enter a map.
+
+ @ref QCBOR_ERR_DUPLICATE_LABEL when one of the labels being searched
+ for is duplicate.
+
+ @ref QCBOR_ERR_HIT_END or other errors classifed as not-well-formed
+ by QCBORDecode_IsNotWellFormed() as it is not possible to traverse
+ maps that have any non-well formed items.
+
+ @ref QCBOR_ERR_UNEXPECTED_TYPE when the type of an item found by
+ matching a label is not the type requested.
+
+ @ref QCBOR_ERR_ARRAY_NESTING_TOO_DEEP and other implementation limit
+  errors as it is not possible to travere a map beyond the limits of
+  the implementation.
+
+ The error may occur on items that are not being searched for.  For
+ example, it is impossible to traverse over a map that has an array in
+ it that is not closed or over array and map nesting deeper than this
+ implementation can track.
+
+ See also QCBORDecode_GetItemInMapN().
+ */
+void QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList);
+
+
+/**
+ @brief Per-item callback for map searching.
+
+ @param[in] pCallbackCtx  Pointer to the caller-defined context for the callback
+ @param[in] pItem  The item from the map.
+
+ The error set is intended for QCBOR errors, not general protocol
+ decoding errors. If this sets other than @ref QCBOR_SUCCESS, the
+ search will stop and the value it returns will be set in
+ QCBORDecode_GetItemsInMapWithCallback(). The special error, @ref
+ QCBOR_ERR_CALLBACK_FAIL, can be returned to indicate some protocol
+ processing error that is not a CBOR error. The specific details of
+ the protocol processing error can be returned the call back context.
+ */
+typedef QCBORError (*QCBORItemCallback)(void            *pCallbackCtx,
+                                        const QCBORItem *pItem);
+
+
+/**
+ @brief Get a group of labeled items all at once from a map with a callback
+
+ @param[in] pCtx   The decode context.
+ @param[in,out] pItemList  On input the items to search for. On output the returned items.
+ @param[in,out] pCallbackCtx Pointer to a context structure for @ref QCBORItemCallback
+ @param[in] pfCB pointer to function of type @ref QCBORItemCallback that is called on unmatched items.
+
+ This searchs a map like QCBORDecode_GetItemsInMap(), but calls a
+ callback on items not matched rather than ignoring them. If @c
+ pItemList is empty, the call back will be called on every item in the
+ map.
+
+ Like QCBORDecode_GetItemsInMap(), this only matches and calls back on
+ the items at the top level of the map entered. Items in nested
+ maps and arrays are skipped over and not candidate for matching or the
+ callback.
+
+ See QCBORItemCallback() for error handling.
+ */
+void QCBORDecode_GetItemsInMapWithCallback(QCBORDecodeContext *pCtx,
+                                           QCBORItem          *pItemList,
+                                           void               *pCallbackCtx,
+                                           QCBORItemCallback   pfCB);
+
+
+
+
+/**
+ @brief Decode the next item as a Boolean.
+
+ @param[in] pCtx   The decode context.
+ @param[out] pbBool  The decoded byte string.
+
+ The CBOR item to decode must be either the CBOR simple value (CBOR
+ type 7) @c true or @c false.
+
+ See @ref Decode-Errors for discussion on how error handling works.  It
+ the CBOR item to decode is not true or false the @ref
+ QCBOR_ERR_UNEXPECTED_TYPE error is set.
+*/
+void QCBORDecode_GetBool(QCBORDecodeContext *pCtx, bool *pbBool);
+
+void QCBORDecode_GetBoolInMapN(QCBORDecodeContext *pCtx,
+                               int64_t             nLabel,
+                               bool               *pbBool);
+
+void QCBORDecode_GetBoolInMapSZ(QCBORDecodeContext *pCtx,
+                                const char         *szLabel,
+                                bool               *pbBool);
+
+
+
+
+
+
+/**
+ @brief Decode the next item as a date string.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pDateString            The decoded URI.
+
+ This decodes the standard CBOR date/time string tag, integer tag
+ number of 0, or encoded CBOR that is not a tag, but borrows the
+ date string content format.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_DATE_STRING, QCBOREncode_AddDateString() and
+ @ref QCBOR_TYPE_DATE_STRING.
+*/
+static void QCBORDecode_GetDateString(QCBORDecodeContext *pCtx,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC         *pDateString);
+
+static void QCBORDecode_GetDateStringInMapN(QCBORDecodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            uint8_t             uTagRequirement,
+                                            UsefulBufC         *pDateString);
+
+static void QCBORDecode_GetDateStringInMapSZ(QCBORDecodeContext *pCtx,
+                                             const char         *szLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBufC         *pDateString);
+
+
+/**
+ @brief Decode the next item as an epoch date.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pnTime            The decoded epoch date.
+
+ This decodes the standard CBOR epoch date/time tag, integer tag
+ number of 1, or encoded CBOR that is not a tag, but borrows the
+ content format.
+
+ This will handle floating-point dates, but always returns them as an
+ @c int64_t discarding the fractional part. Use QCBORDecode_GetNext()
+ instead of this to get the fractional part.
+
+ Floating-point dates that are plus infinity, minus infinity or NaN
+ (not-a-number) will result in the @ref QCBOR_ERR_DATE_OVERFLOW
+ error. If the QCBOR library is compiled with floating-point disabled,
+ @ref QCBOR_ERR_HW_FLOAT_DISABLED is set. If compiled with preferred
+ float disabled, half-precision dates will result in the @ref
+ QCBOR_ERR_HALF_PRECISION_DISABLED error.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_DATE_EPOCH, QCBOREncode_AddDateEpoch() and
+ @ref QCBOR_TYPE_DATE_EPOCH.
+*/
+void QCBORDecode_GetEpochDate(QCBORDecodeContext *pCtx,
+                              uint8_t             uTagRequirement,
+                              int64_t            *pnTime);
+
+void QCBORDecode_GetEpochDateInMapN(QCBORDecodeContext *pCtx,
+                                    int64_t             nLabel,
+                                    uint8_t             uTagRequirement,
+                                    int64_t            *pnTime);
+
+void QCBORDecode_GetEpochDateInMapSZ(QCBORDecodeContext *pCtx,
+                                     const char         *szLabel,
+                                     uint8_t             uTagRequirement,
+                                     int64_t            *pnTime);
+
+
+
+
+/**
+ @brief Decode the next item as a big number.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pValue          The returned big number.
+ @param[out] pbIsNegative    Is @c true if the big number is negative. This
+                             is only valid when @c uTagRequirement is
+                             @ref QCBOR_TAG_REQUIREMENT_TAG.
+
+ This decodes a standard CBOR big number, integer tag number of 2 or
+ 3, or encoded CBOR that is not a tag, but borrows the content format.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ The big number is in network byte order. The first byte in @c pValue
+ is the most significant byte. There may be leading zeros.
+
+ The negative value is computed as -1 - n, where n is the postive big
+ number in @c pValue. There is no standard representation for
+ big numbers, positive or negative in C, so this implementation leaves
+ it up to the caller to apply this computation for negative big numbers.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ Determination of the sign of the big number depends on the tag
+ requirement of the protocol using the big number. If the protocol
+ requires tagging, @ref QCBOR_TAG_REQUIREMENT_TAG, then the sign
+ indication is in the protocol and @c pbIsNegative indicates the
+ sign. If the protocol doesn't use a tag, @ref
+ QCBOR_TAG_REQUIREMENT_NOT_A_TAG, then the protocol design must have some
+ way of indicating the sign.
+
+ See also QCBORDecode_GetInt64ConvertAll(),
+ QCBORDecode_GetUInt64ConvertAll() and
+ QCBORDecode_GetDoubleConvertAll() which can convert big numbers.
+
+ See also @ref CBOR_TAG_POS_BIGNUM, @ref CBOR_TAG_NEG_BIGNUM,
+ QCBOREncode_AddPositiveBignum(), QCBOREncode_AddNegativeBignum(),
+ @ref QCBOR_TYPE_POSBIGNUM and @ref QCBOR_TYPE_NEGBIGNUM.
+
+*/
+// Improvement: Add function that converts integers and other to big nums
+void QCBORDecode_GetBignum(QCBORDecodeContext *pCtx,
+                           uint8_t             uTagRequirement,
+                           UsefulBufC         *pValue,
+                           bool               *pbIsNegative);
+
+void QCBORDecode_GetBignumInMapN(QCBORDecodeContext *pCtx,
+                                 int64_t             nLabel,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pValue,
+                                 bool               *pbIsNegative);
+
+void QCBORDecode_GetBignumInMapSZ(QCBORDecodeContext *pCtx,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC         *pValue,
+                                  bool               *pbIsNegative);
+
+
+
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+/**
+ @brief Decode the next item as a decimal fraction.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pnMantissa      The mantissa.
+ @param[out] pnExponent      The base 10 exponent.
+
+ This decodes a standard CBOR decimal fraction, integer tag number of
+ 4, or encoded CBOR that is not a tag, but borrows the content format.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ The  value of this is computed by:
+
+     mantissa * ( 10 ** exponent )
+
+ In the encoded CBOR, the mantissa and exponent may be of CBOR type 0
+ (positive integer), type 1 (negative integer), type 2 tag 2 (positive
+ big number) or type 2 tag 3 (negative big number). This
+ implementation will attempt to convert all of these to an @c
+ int64_t. If the value won't fit, @ref
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW or
+ QCBOR_ERR_BAD_EXP_AND_MANTISSA will be set.
+
+ This implementation limits the exponent to between @c INT64_MIN and
+ @c INT64_MAX while CBOR allows the range of @c -UINT64_MAX to
+ @c UINT64_MAX.
+
+ Various format and type issues will result in @ref
+ QCBOR_ERR_BAD_EXP_AND_MANTISSA being set.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also QCBORDecode_GetInt64ConvertAll(),
+ QCBORDecode_GetUInt64ConvertAll() and
+ QCBORDecode_GetDoubleConvertAll() which can convert big numbers.
+
+ See also @ref CBOR_TAG_DECIMAL_FRACTION,
+ QCBOREncode_AddDecimalFraction(), @ref QCBOR_TYPE_DECIMAL_FRACTION
+ and QCBORDecode_GetDecimalFractionBig().
+*/
+void QCBORDecode_GetDecimalFraction(QCBORDecodeContext *pCtx,
+                                    uint8_t             uTagRequirement,
+                                    int64_t            *pnMantissa,
+                                    int64_t            *pnExponent);
+
+void QCBORDecode_GetDecimalFractionInMapN(QCBORDecodeContext *pCtx,
+                                          int64_t             nLabel,
+                                          uint8_t             uTagRequirement,
+                                          int64_t            *pnMantissa,
+                                          int64_t            *pnExponent);
+
+void QCBORDecode_GetDecimalFractionInMapSZ(QCBORDecodeContext *pMe,
+                                           const char         *szLabel,
+                                           uint8_t             uTagRequirement,
+                                           int64_t            *pnMantissa,
+                                           int64_t            *pnExponent);
+
+
+/**
+ @brief Decode the next item as a decimal fraction with a big number mantissa.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[in] MantissaBuffer The buffer in which to put the mantissa.
+ @param[out] pMantissa      The big num mantissa.
+ @param[out] pbMantissaIsNegative  Is @c true if @c pMantissa is negative.
+ @param[out] pnExponent      The base 10 exponent.
+
+ This is the same as QCBORDecode_GetDecimalFraction() except the
+ mantissa is returned as a big number.
+
+ In the encoded CBOR, the mantissa may be a type 0 (positive integer),
+ type 1 (negative integer), type 2 tag 2 (positive big number) or type
+ 2 tag 3 (negative big number). This implementation will convert all
+ these to a big number. The limit to this conversion is the size of @c
+ MantissaBuffer.
+
+ See also QCBORDecode_GetInt64ConvertAll(),
+ QCBORDecode_GetUInt64ConvertAll() and
+ QCBORDecode_GetDoubleConvertAll() which can convert decimal
+ fractions.
+
+ See also @ref CBOR_TAG_DECIMAL_FRACTION,
+ QCBOREncode_AddDecimalFraction(), @ref QCBOR_TYPE_DECIMAL_FRACTION
+ and QCBORDecode_GetDecimalFraction().
+*/
+void QCBORDecode_GetDecimalFractionBig(QCBORDecodeContext *pCtx,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBuf           MantissaBuffer,
+                                       UsefulBufC         *pMantissa,
+                                       bool               *pbMantissaIsNegative,
+                                       int64_t            *pnExponent);
+
+void QCBORDecode_GetDecimalFractionBigInMapN(QCBORDecodeContext *pCtx,
+                                             int64_t             nLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBuf           MantissaBuffer,
+                                             UsefulBufC         *pbMantissaIsNegative,
+                                             bool               *pbIsNegative,
+                                             int64_t            *pnExponent);
+
+void QCBORDecode_GetDecimalFractionBigInMapSZ(QCBORDecodeContext *pCtx,
+                                              const char         *szLabel,
+                                              uint8_t             uTagRequirement,
+                                              UsefulBuf           MantissaBuffer,
+                                              UsefulBufC         *pMantissa,
+                                              bool               *pbMantissaIsNegative,
+                                              int64_t            *pnExponent);
+
+
+/**
+ @brief Decode the next item as a big float.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pnMantissa      The mantissa.
+ @param[out] pnExponent      The base 2 exponent.
+
+ This decodes a standard CBOR big float, integer tag number of 5, or
+ encoded CBOR that is not a tag, but borrows the content format.
+
+ This is the same as QCBORDecode_GetDecimalFraction() with the
+ important distinction that the value is computed by:
+
+     mantissa * ( 2 ** exponent )
+
+ See also QCBORDecode_GetInt64ConvertAll(),
+ QCBORDecode_GetUInt64ConvertAll() and
+ QCBORDecode_GetDoubleConvertAll() which can convert big floats.
+
+ See also @ref CBOR_TAG_BIGFLOAT, QCBOREncode_AddBigFloat(), @ref
+ QCBOR_TYPE_BIGFLOAT and QCBORDecode_GetBigFloatBig().
+ */
+void QCBORDecode_GetBigFloat(QCBORDecodeContext *pCtx,
+                             uint8_t             uTagRequirement,
+                             int64_t            *pnMantissa,
+                             int64_t            *pnExponent);
+
+void QCBORDecode_GetBigFloatInMapN(QCBORDecodeContext *pCtx,
+                                   int64_t             nLabel,
+                                   uint8_t             uTagRequirement,
+                                   int64_t            *pnMantissa,
+                                   int64_t            *pnExponent);
+
+void QCBORDecode_GetBigFloatInMapSZ(QCBORDecodeContext *pCtx,
+                                    const char         *szLabel,
+                                    uint8_t             uTagRequirement,
+                                    int64_t            *pnMantissa,
+                                    int64_t            *pnExponent);
+
+
+/**
+ @brief Decode the next item as a big float with a big number mantissa.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[in] MantissaBuffer The buffer in which to put the mantissa.
+ @param[out] pMantissa      The big num mantissa.
+ @param[out] pbMantissaIsNegative  Is @c true if @c pMantissa is negative.
+ @param[out] pnExponent      The base 2 exponent.
+
+ This is the same as QCBORDecode_GetDecimalFractionBig() with the
+ important distinction that the value is computed by:
+
+     mantissa * ( 2 ** exponent )
+
+ See also QCBORDecode_GetInt64ConvertAll(),
+ QCBORDecode_GetUInt64ConvertAll() and
+ QCBORDecode_GetDoubleConvertAll() which can convert big floats.
+
+ See also @ref CBOR_TAG_BIGFLOAT, QCBOREncode_AddBigFloat(),
+ @ref QCBOR_TYPE_BIGFLOAT and QCBORDecode_GetBigFloat().
+ */
+void QCBORDecode_GetBigFloatBig(QCBORDecodeContext *pCtx,
+                                uint8_t             uTagRequirement,
+                                UsefulBuf           MantissaBuffer,
+                                UsefulBufC         *pMantissa,
+                                bool               *pbMantissaIsNegative,
+                                int64_t            *pnExponent);
+
+void QCBORDecode_GetBigFloatBigInMapN(QCBORDecodeContext *pCtx,
+                                      int64_t             nLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBuf           MantissaBuffer,
+                                      UsefulBufC         *pMantissa,
+                                      bool               *pbMantissaIsNegative,
+                                      int64_t            *pnExponent);
+
+void QCBORDecode_GetBigFloatBigInMapSZ(QCBORDecodeContext *pCtx,
+                                       const char         *szLabel,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBuf           MantissaBuffer,
+                                       UsefulBufC         *pMantissa,
+                                       bool               *pbMantissaIsNegative,
+                                       int64_t            *pnExponent);
+#endif /* #ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+
+
+
+/**
+ @brief Decode the next item as a URI.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pURI            The decoded URI.
+
+ This decodes a standard CBOR URI tag, integer tag number of 32,
+ or encoded CBOR that is not a tag, that is a URI encoded in a text string.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_URI, QCBOREncode_AddURI() and
+ @ref QCBOR_TYPE_URI.
+ */
+static void QCBORDecode_GetURI(QCBORDecodeContext *pCtx,
+                               uint8_t             uTagRequirement,
+                               UsefulBufC         *pURI);
+
+static void QCBORDecode_GetURIInMapN(QCBORDecodeContext *pCtx,
+                                     int64_t             nLabel,
+                                     uint8_t             uTagRequirement,
+                                     UsefulBufC         *pURI);
+
+static void QCBORDecode_GetURIInMapSZ(QCBORDecodeContext *pCtx,
+                                      const char *        szLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC         *pURI);
+
+
+/**
+ @brief Decode the next item as base64 encoded text.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pB64Text          The decoded base64 text.
+
+ This decodes a standard CBOR base64 tag, integer tag number of 34, or
+ encoded CBOR that is not a tag, that is base64 encoded bytes encoded
+ in a text string.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ Note that this does not actually remove the base64 encoding.
+
+ See also @ref CBOR_TAG_B64, QCBOREncode_AddB64Text() and
+ @ref QCBOR_TYPE_BASE64.
+*/
+static void QCBORDecode_GetB64(QCBORDecodeContext *pCtx,
+                               uint8_t             uTagRequirement,
+                               UsefulBufC         *pB64Text);
+
+static void QCBORDecode_GetB64InMapN(QCBORDecodeContext *pCtx,
+                                     int64_t             nLabel,
+                                     uint8_t             uTagRequirement,
+                                     UsefulBufC         *pB64Text);
+
+static void QCBORDecode_GetB64InMapSZ(QCBORDecodeContext *pCtx,
+                                      const char         *szLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC         *pB64Text);
+
+/**
+ @brief Decode the next item as base64URL encoded text.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pB64Text          The decoded base64 text.
+
+ This decodes a standard CBOR base64url tag, integer tag number of 33,
+ or encoded CBOR that is not a tag, that is base64url encoded bytes
+ encoded in a text string.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ Note that this does not actually remove the base64url encoding.
+
+ See also @ref CBOR_TAG_B64URL, QCBOREncode_AddB64URLText() and
+ @ref QCBOR_TYPE_BASE64URL.
+*/
+static void QCBORDecode_GetB64URL(QCBORDecodeContext *pCtx,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC         *pB64Text);
+
+static void QCBORDecode_GetB64URLInMapN(QCBORDecodeContext *pCtx,
+                                        int64_t             nLabel,
+                                        uint8_t             uTagRequirement,
+                                        UsefulBufC         *pB64Text);
+
+static void QCBORDecode_GetB64URLInMapSZ(QCBORDecodeContext *pCtx,
+                                         const char         *szLabel,
+                                         uint8_t             uTagRequirement,
+                                         UsefulBufC         *pB64Text);
+
+/**
+ @brief Decode the next item as a regular expression.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pRegex          The decoded regular expression.
+
+ This decodes a standard CBOR regex tag, integer tag number of 35, or
+ encoded CBOR that is not a tag, that is a PERL-compatible regular
+ expression encoded in a text string.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_REGEX, QCBOREncode_AddRegex() and
+ @ref QCBOR_TYPE_REGEX.
+ */
+static void QCBORDecode_GetRegex(QCBORDecodeContext *pCtx,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pRegex);
+
+static void QCBORDecode_GetRegexInMapN(QCBORDecodeContext *pCtx,
+                                       int64_t             nLabel,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBufC         *pRegex);
+
+static void QCBORDecode_GetRegexInMapSZ(QCBORDecodeContext *pCtx,
+                                        const char *        szLabel,
+                                        uint8_t             uTagRequirement,
+                                        UsefulBufC         *pRegex);
+
+
+/**
+ @brief Decode the next item as a MIME message.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pMessage        The decoded regular expression.
+ @param[out] pbIsTag257     @c true if tag was 257. May be @c NULL.
+
+ This decodes the standard CBOR MIME and binary MIME tags, integer tag
+ numbers of 36 or 257, or encoded CBOR that is not a tag, that is a
+ MIME message encoded in a text or binary string.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ The MIME message itself is not parsed.
+
+ This decodes both tag 36 and 257. If it is tag 257, pbIsTag257
+ is @c true. The difference between the two is that
+ tag 36 is utf8 and tag 257 is a byte string that can
+ carry binary MIME. QCBOR processes them exactly
+ the same. Possibly the difference can be ignored.
+ NULL can be passed to have no value returned.
+
+ See also @ref CBOR_TAG_MIME, @ref CBOR_TAG_BINARY_MIME,
+ QCBOREncode_AddTMIMEData(), @ref QCBOR_TYPE_MIME and
+ @ref QCBOR_TYPE_BINARY_MIME.
+
+ This does no translation of line endings. See QCBOREncode_AddText()
+ for a discussion of line endings in CBOR.
+*/
+static void QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pCtx,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBufC         *pMessage,
+                                       bool               *pbIsTag257);
+
+static void QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pCtx,
+                                            int64_t              nLabel,
+                                            uint8_t              uTagRequirement,
+                                            UsefulBufC          *pMessage,
+                                            bool                *pbIsTag257);
+
+
+static void QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pCtx,
+                                              const char         *szLabel,
+                                              uint8_t             uTagRequirement,
+                                              UsefulBufC         *pMessage,
+                                              bool               *pbIsTag257);
+
+/**
+ @brief Decode the next item as a UUID
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pUUID            The decoded UUID
+
+ This decodes a standard CBOR UUID tag, integer tag number of 37, or
+ encoded CBOR that is not a tag, that is a UUID encoded in a byte
+ string.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_BIN_UUID, QCBOREncode_AddBinaryUUID() and
+ @ref QCBOR_TYPE_UUID.
+ */
+static inline void QCBORDecode_GetBinaryUUID(QCBORDecodeContext *pCtx,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBufC         *pUUID);
+
+inline static void QCBORDecode_GetBinaryUUIDInMapN(QCBORDecodeContext *pCtx,
+                                                   int64_t             nLabel,
+                                                   uint8_t             uTagRequirement,
+                                                   UsefulBufC         *pUUID);
+
+inline static void QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pCtx,
+                                                    const char         *szLabel,
+                                                    uint8_t             uTagRequirement,
+                                                    UsefulBufC         *pUUID);
+
+
+
+/**
+ @brief Decode some byte-string wrapped CBOR.
+
+ @param[in] pCtx   The decode context.
+ @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pBstr  Pointer and length of byte-string wrapped CBOR (optional).
+
+ This is for use on some CBOR that has been wrapped in a byte
+ string. There are several ways that this can occur.
+
+ First is tag 24 and tag 63. Tag 24 wraps a single CBOR data item and
+ 63 a CBOR sequence.  This implementation doesn't distinguish between
+ the two (it would be more code and doesn't seem important).
+
+ The @ref Tag-Usage discussion on the tag requirement applies here
+ just the same as any other tag.
+
+ In other cases, CBOR is wrapped in a byte string, but it is
+ identified as CBOR by other means. The contents of a COSE payload are
+ one example of that. They can be identified by the COSE content type,
+ or they can be identified as CBOR indirectly by the protocol that
+ uses COSE. for example, if a blob of CBOR is identified as a CWT,
+ then the COSE payload is CBOR.  To enter into CBOR of this type use
+ the @ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG as the \c uTagRequirement
+ argument.
+
+ Note that byte string wrapped CBOR can also be decoded by getting the
+ byte string with QCBORDecode_GetItem() or QCBORDecode_GetByteString()
+ and feeding it into another instance of QCBORDecode. Doing it with
+ this function has the advantage of using less memory as another
+ instance of QCBORDecode is not necessary.
+
+ When the wrapped CBOR is entered with this function, the pre-order
+ traversal and such are bounded to the wrapped
+ CBOR. QCBORDecode_ExitBstrWrapped() must be called resume processing
+ CBOR outside the wrapped CBOR.
+
+ If @c pBstr is not @c NULL the pointer and length of the wrapped
+ CBOR will be returned. This is usually not needed, but sometimes
+ useful, particularly in the case of verifying signed data like the
+ COSE payload. This is usually the pointer and length of the
+ data is that is hashed or MACed.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See also QCBORDecode_ExitBstrWrapped(), QCBORDecode_EnterMap() and
+ QCBORDecode_EnterArray().
+
+ */
+void QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pCtx,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC         *pBstr);
+
+void QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pCtx,
+                                          int64_t             nLabel,
+                                          uint8_t             uTagRequirement,
+                                          UsefulBufC         *pBstr);
+
+void QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pCtx,
+                                           const char         *szLabel,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC         *pBstr);
+
+
+/**
+ @brief Exit some bstr-wrapped CBOR  has been enetered.
+
+ @param[in] pCtx   The decode context.
+
+ Bstr-wrapped CBOR must have been entered for this to succeed.
+
+ The items in the wrapped CBOR that was entered do not have to have been
+ consumed for this to succeed.
+
+ The this sets thepre-order traversal cursor to the item after
+ the byte string that was exited.
+*/
+void QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pCtx);
+
+
+
+
+/* ===========================================================================
+   BEGINNING OF PRIVATE INLINE IMPLEMENTATION
+   ========================================================================== */
+
+
+// Semi-private
+void
+QCBORDecode_GetUInt64ConvertInternal(QCBORDecodeContext *pMe,
+                                     uint32_t            uConvertTypes,
+                                     uint64_t           *puValue,
+                                     QCBORItem          *pItem);
+
+// Semi-private
+void
+QCBORDecode_GetUInt64ConvertInternalInMapN(QCBORDecodeContext *pMe,
+                                           int64_t             nLabel,
+                                           uint32_t            uConvertTypes,
+                                           uint64_t           *puValue,
+                                           QCBORItem          *pItem);
+
+// Semi-private
+void
+QCBORDecode_GetUInt64ConvertInternalInMapSZ(QCBORDecodeContext *pMe,
+                                            const char         *szLabel,
+                                            uint32_t            uConvertTypes,
+                                            uint64_t           *puValue,
+                                            QCBORItem          *pItem);
+
+
+void QCBORDecode_GetUInt64Convert(QCBORDecodeContext *pMe,
+                                  uint32_t            uConvertTypes,
+                                  uint64_t           *puValue)
+{
+    QCBORItem Item;
+    QCBORDecode_GetUInt64ConvertInternal(pMe, uConvertTypes, puValue, &Item);
+}
+
+inline static void
+QCBORDecode_GetUInt64ConvertInMapN(QCBORDecodeContext *pMe,
+                                   int64_t             nLabel,
+                                   uint32_t            uConvertTypes,
+                                   uint64_t           *puValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetUInt64ConvertInternalInMapN(pMe,
+                                              nLabel,
+                                              uConvertTypes,
+                                              puValue,
+                                              &Item);
+}
+
+inline static void
+QCBORDecode_GetUInt64ConvertInMapSZ(QCBORDecodeContext *pMe,
+                                    const char         *szLabel,
+                                    uint32_t            uConvertTypes,
+                                    uint64_t           *puValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetUInt64ConvertInternalInMapSZ(pMe,
+                                               szLabel,
+                                               uConvertTypes,
+                                               puValue,
+                                               &Item);
+}
+
+static inline void
+QCBORDecode_GetUInt64(QCBORDecodeContext *pMe, uint64_t *puValue)
+{
+    QCBORDecode_GetUInt64Convert(pMe, QCBOR_CONVERT_TYPE_XINT64, puValue);
+}
+
+inline static void
+QCBORDecode_GetUInt64InMapN(QCBORDecodeContext *pMe,
+                            int64_t             nLabel,
+                            uint64_t           *puValue)
+{
+   QCBORDecode_GetUInt64ConvertInMapN(pMe,
+                                      nLabel,
+                                      QCBOR_CONVERT_TYPE_XINT64,
+                                      puValue);
+}
+
+inline static void
+QCBORDecode_GetUInt64InMapSZ(QCBORDecodeContext *pMe,
+                             const char         *szLabel,
+                             uint64_t           *puValue)
+{
+   QCBORDecode_GetUInt64ConvertInMapSZ(pMe,
+                                       szLabel,
+                                       QCBOR_CONVERT_TYPE_XINT64,
+                                       puValue);
+}
+
+
+// Semi-private
+void QCBORDecode_EnterBoundedMapOrArray(QCBORDecodeContext *pMe, uint8_t uType, QCBORItem *pItem);
+
+// Semi-private
+inline static void QCBORDecode_EnterMap(QCBORDecodeContext *pMe, QCBORItem *pItem) {
+   QCBORDecode_EnterBoundedMapOrArray(pMe, QCBOR_TYPE_MAP, pItem);
+}
+
+// Semi-private
+inline static void QCBORDecode_EnterArray(QCBORDecodeContext *pMe, QCBORItem *pItem) {
+   QCBORDecode_EnterBoundedMapOrArray(pMe, QCBOR_TYPE_ARRAY, pItem);
+}
+
+// Semi-private
+void QCBORDecode_ExitBoundedMapOrArray(QCBORDecodeContext *pMe, uint8_t uType);
+
+
+static inline void QCBORDecode_ExitArray(QCBORDecodeContext *pMe)
+{
+   QCBORDecode_ExitBoundedMapOrArray(pMe, QCBOR_TYPE_ARRAY);
+}
+
+static inline void QCBORDecode_ExitMap(QCBORDecodeContext *pMe)
+{
+   QCBORDecode_ExitBoundedMapOrArray(pMe, QCBOR_TYPE_MAP);
+}
+
+
+// Semi-private
+void
+QCBORDecode_GetInt64ConvertInternal(QCBORDecodeContext *pMe,
+                                    uint32_t            uConvertTypes,
+                                    int64_t            *pnValue,
+                                    QCBORItem          *pItem);
+
+// Semi-private
+void
+QCBORDecode_GetInt64ConvertInternalInMapN(QCBORDecodeContext *pMe,
+                                          int64_t             nLabel,
+                                          uint32_t            uConvertTypes,
+                                          int64_t            *pnValue,
+                                          QCBORItem          *pItem);
+
+// Semi-private
+void
+QCBORDecode_GetInt64ConvertInternalInMapSZ(QCBORDecodeContext *pMe,
+                                           const char         *szLabel,
+                                           uint32_t            uConvertTypes,
+                                           int64_t            *pnValue,
+                                           QCBORItem          *pItem);
+
+inline static void
+QCBORDecode_GetInt64Convert(QCBORDecodeContext *pMe,
+                            uint32_t            uConvertTypes,
+                            int64_t            *pnValue)
+{
+    QCBORItem Item;
+    QCBORDecode_GetInt64ConvertInternal(pMe, uConvertTypes, pnValue, &Item);
+}
+
+inline static void
+QCBORDecode_GetInt64ConvertInMapN(QCBORDecodeContext *pMe,
+                                  int64_t             nLabel,
+                                  uint32_t            uConvertTypes,
+                                  int64_t            *pnValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetInt64ConvertInternalInMapN(pMe,
+                                             nLabel,
+                                             uConvertTypes,
+                                             pnValue,
+                                             &Item);
+}
+
+inline static void
+QCBORDecode_GetInt64ConvertInMapSZ(QCBORDecodeContext *pMe,
+                                   const char         *szLabel,
+                                   uint32_t            uConvertTypes,
+                                   int64_t            *pnValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetInt64ConvertInternalInMapSZ(pMe,
+                                              szLabel,
+                                              uConvertTypes,
+                                              pnValue,
+                                              &Item);
+}
+
+inline static void
+QCBORDecode_GetInt64(QCBORDecodeContext *pMe, int64_t *pnValue)
+{
+    QCBORDecode_GetInt64Convert(pMe, QCBOR_CONVERT_TYPE_XINT64, pnValue);
+}
+
+inline static void
+QCBORDecode_GetInt64InMapN(QCBORDecodeContext *pMe,
+                           int64_t             nLabel,
+                           int64_t            *pnValue)
+{
+   QCBORDecode_GetInt64ConvertInMapN(pMe,
+                                     nLabel,
+                                     QCBOR_CONVERT_TYPE_XINT64,
+                                     pnValue);
+}
+
+inline static void
+QCBORDecode_GetInt64InMapSZ(QCBORDecodeContext *pMe,
+                            const char         *szLabel,
+                            int64_t            *pnValue)
+{
+   QCBORDecode_GetInt64ConvertInMapSZ(pMe,
+                                      szLabel,
+                                      QCBOR_CONVERT_TYPE_XINT64,
+                                      pnValue);
+}
+
+
+
+
+
+// Semi-private
+void
+QCBORDecode_GetDoubleConvertInternal(QCBORDecodeContext *pMe,
+                                     uint32_t            uConvertTypes,
+                                     double             *pValue,
+                                     QCBORItem          *pItem);
+
+// Semi-private
+void
+QCBORDecode_GetDoubleConvertInternalInMapN(QCBORDecodeContext *pMe,
+                                           int64_t             nLabel,
+                                           uint32_t            uConvertTypes,
+                                           double             *pdValue,
+                                           QCBORItem          *pItem);
+
+// Semi-private
+void
+QCBORDecode_GetDoubleConvertInternalInMapSZ(QCBORDecodeContext *pMe,
+                                            const char         *szLabel,
+                                            uint32_t            uConvertTypes,
+                                            double             *pdValue,
+                                            QCBORItem          *pItem);
+
+
+inline static void
+QCBORDecode_GetDoubleConvert(QCBORDecodeContext *pMe,
+                             uint32_t            uConvertTypes,
+                             double             *pdValue)
+{
+    QCBORItem Item;
+    QCBORDecode_GetDoubleConvertInternal(pMe, uConvertTypes, pdValue, &Item);
+}
+
+inline static void
+QCBORDecode_GetDoubleConvertInMapN(QCBORDecodeContext *pMe,
+                                   int64_t             nLabel,
+                                   uint32_t            uConvertTypes,
+                                   double             *pdValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetDoubleConvertInternalInMapN(pMe,
+                                              nLabel,
+                                              uConvertTypes,
+                                              pdValue,
+                                              &Item);
+}
+
+inline static void
+QCBORDecode_GetDoubleConvertInMapSZ(QCBORDecodeContext *pMe,
+                                    const char         *szLabel,
+                                    uint32_t            uConvertTypes,
+                                    double             *pdValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetDoubleConvertInternalInMapSZ(pMe,
+                                               szLabel,
+                                               uConvertTypes,
+                                               pdValue,
+                                               &Item);
+}
+
+inline static void
+QCBORDecode_GetDouble(QCBORDecodeContext *pMe, double *pValue)
+{
+    QCBORDecode_GetDoubleConvert(pMe, QCBOR_CONVERT_TYPE_FLOAT, pValue);
+}
+
+inline static void
+QCBORDecode_GetDoubleInMapN(QCBORDecodeContext *pMe,
+                            int64_t             nLabel,
+                            double             *pdValue)
+{
+   QCBORDecode_GetDoubleConvertInMapN(pMe,
+                                      nLabel,
+                                      QCBOR_CONVERT_TYPE_FLOAT,
+                                      pdValue);
+}
+
+inline static void
+QCBORDecode_GetDoubleInMapSZ(QCBORDecodeContext *pMe,
+                             const char         *szLabel,
+                             double             *pdValue)
+{
+   QCBORDecode_GetDoubleConvertInMapSZ(pMe,
+                                       szLabel,
+                                       QCBOR_CONVERT_TYPE_FLOAT,
+                                       pdValue);
+}
+
+
+
+// Semi private (this may change in the future)
+#define QCBOR_TAGSPEC_NUM_TYPES 4
+/* Improvement:  Carefully understand what compilers do with this,
+particularly initialization and see if it can be optimized so
+there is less code and maybe so it can be smaller. */
+typedef struct {
+   /* One of QCBOR_TAGSPEC_MATCH_xxx */
+   uint8_t uTagRequirement;
+   /* The tagged type translated into QCBOR_TYPE_XXX. Used to match explicit
+      tagging */
+   uint8_t uTaggedTypes[QCBOR_TAGSPEC_NUM_TYPES];
+   /* The types of the content, which are used to match implicit tagging */
+   uint8_t uAllowedContentTypes[QCBOR_TAGSPEC_NUM_TYPES];
+} TagSpecification;
+
+// Semi private
+void QCBORDecode_GetTaggedStringInternal(QCBORDecodeContext *pMe,
+                                         TagSpecification    TagSpec,
+                                         UsefulBufC         *pBstr);
+
+
+// Semi private
+void QCBORDecode_GetTaggedStringInMapN(QCBORDecodeContext *pMe,
+                                       int64_t             nLabel,
+                                       TagSpecification    TagSpec,
+                                       UsefulBufC         *pString);
+
+// Semi private
+void QCBORDecode_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe,
+                                        const char *        szLabel,
+                                        TagSpecification    TagSpec,
+                                        UsefulBufC         *pString);
+
+
+// Semi private
+QCBORError QCBORDecode_GetMIMEInternal(uint8_t           uTagRequirement,
+                                       const  QCBORItem *pItem,
+                                       UsefulBufC       *pMessage,
+                                       bool             *pbIsTag257);
+
+
+
+static inline void
+QCBORDecode_GetByteString(QCBORDecodeContext *pMe,  UsefulBufC *pValue)
+{
+   // Complier should make this just 64-bit integer parameter
+   const TagSpecification TagSpec =
+      {
+         QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pValue);
+}
+
+inline static void
+QCBORDecode_GetByteStringInMapN(QCBORDecodeContext *pMe,
+                                int64_t             nLabel,
+                                UsefulBufC         *pBstr)
+{
+   const TagSpecification TagSpec =
+      {
+         QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pBstr);
+}
+
+inline static void
+QCBORDecode_GetByteStringInMapSZ(QCBORDecodeContext *pMe,
+                                 const char         *szLabel,
+                                 UsefulBufC         *pBstr)
+{
+   const TagSpecification TagSpec =
+      {
+         QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pBstr);
+}
+
+
+static inline void
+QCBORDecode_GetTextString(QCBORDecodeContext *pMe,  UsefulBufC *pValue)
+{
+   // Complier should make this just 64-bit integer parameter
+   const TagSpecification TagSpec =
+      {
+         QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pValue);
+}
+
+inline static void
+QCBORDecode_GetTextStringInMapN(QCBORDecodeContext *pMe,
+                                int64_t             nLabel,
+                                UsefulBufC         *pText)
+{
+   // This TagSpec only matches text strings; it also should optimize down
+   // to passing a 64-bit integer
+   const TagSpecification TagSpec =
+      {
+         QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pText);
+}
+
+inline static void
+QCBORDecode_GetTextStringInMapSZ(QCBORDecodeContext *pMe,
+                                 const               char *szLabel,
+                                 UsefulBufC         *pText)
+{
+   const TagSpecification TagSpec =
+      {
+         QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pText);
+}
+
+
+static inline void
+QCBORDecode_GetDateString(QCBORDecodeContext *pMe,
+                          uint8_t             uTagRequirement,
+                          UsefulBufC         *pValue)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_DATE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pValue);
+}
+
+inline static void
+QCBORDecode_GetDateStringInMapN(QCBORDecodeContext *pMe,
+                                int64_t             nLabel,
+                                uint8_t             uTagRequirement,
+                                UsefulBufC         *pText)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_DATE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pText);
+}
+
+inline static void
+QCBORDecode_GetDateStringInMapSZ(QCBORDecodeContext *pMe,
+                                 const char         *szLabel,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pText)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_DATE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pText);
+}
+
+
+static inline void QCBORDecode_GetURI(QCBORDecodeContext *pMe,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC         *pUUID)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_URI, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pUUID);
+}
+
+inline static void
+QCBORDecode_GetURIInMapN(QCBORDecodeContext *pMe,
+                         int64_t             nLabel,
+                         uint8_t             uTagRequirement,
+                         UsefulBufC         *pUUID)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_URI, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pUUID);
+}
+
+inline static void
+QCBORDecode_GetURIInMapSZ(QCBORDecodeContext *pMe,
+                          const char         *szLabel,
+                          uint8_t             uTagRequirement,
+                          UsefulBufC         *pUUID)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_URI, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pUUID);
+}
+
+
+static inline void QCBORDecode_GetB64(QCBORDecodeContext *pMe,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC         *pB64Text)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_BASE64, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pB64Text);
+}
+
+inline static void QCBORDecode_GetB64InMapN(QCBORDecodeContext *pMe,
+                                            int64_t             nLabel,
+                                            uint8_t             uTagRequirement,
+                                            UsefulBufC         *pB64Text)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_BASE64, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pB64Text);
+}
+
+inline static void
+QCBORDecode_GetB64InMapSZ(QCBORDecodeContext *pMe,
+                          const char         *szLabel,
+                          uint8_t             uTagRequirement,
+                          UsefulBufC         *pB64Text)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_BASE64, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pB64Text);
+}
+
+
+static inline void
+QCBORDecode_GetB64URL(QCBORDecodeContext *pMe,
+                      uint8_t             uTagRequirement,
+                      UsefulBufC         *pB64Text)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_BASE64URL, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pB64Text);
+}
+
+inline static void
+QCBORDecode_GetB64URLInMapN(QCBORDecodeContext *pMe,
+                            int64_t             nLabel,
+                            uint8_t             uTagRequirement,
+                            UsefulBufC         *pB64Text)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_BASE64URL, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pB64Text);
+}
+
+inline static void
+QCBORDecode_GetB64URLInMapSZ(QCBORDecodeContext *pMe,
+                             const char         *szLabel,
+                             uint8_t             uTagRequirement,
+                             UsefulBufC         *pB64Text)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_BASE64URL, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pB64Text);
+}
+
+
+static inline void QCBORDecode_GetRegex(QCBORDecodeContext *pMe,
+                                        uint8_t             uTagRequirement,
+                                        UsefulBufC         *pRegex)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_REGEX, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pRegex);
+}
+
+static inline void
+QCBORDecode_GetRegexInMapN(QCBORDecodeContext *pMe,
+                           int64_t             nLabel,
+                           uint8_t             uTagRequirement,
+                           UsefulBufC         *pRegex)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_REGEX, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pRegex);
+}
+
+static inline void
+QCBORDecode_GetRegexInMapSZ(QCBORDecodeContext *pMe,
+                            const char *        szLabel,
+                            uint8_t             uTagRequirement,
+                            UsefulBufC         *pRegex)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_REGEX, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pRegex);
+}
+
+
+static inline void
+QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pMe,
+                           uint8_t             uTagRequirement,
+                           UsefulBufC         *pMessage,
+                           bool               *pbIsTag257)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state, do nothing
+      return;
+   }
+
+   QCBORItem  Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError != QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)QCBORDecode_GetMIMEInternal(uTagRequirement,
+                                                          &Item,
+                                                          pMessage,
+                                                          pbIsTag257);
+}
+
+static inline void
+QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pMe,
+                                 int64_t             nLabel,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pMessage,
+                                 bool               *pbIsTag257)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)QCBORDecode_GetMIMEInternal(uTagRequirement,
+                                                             &Item,
+                                                             pMessage,
+                                                             pbIsTag257);
+   }
+}
+
+static inline void
+QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pMe,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC         *pMessage,
+                                  bool               *pbIsTag257)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)QCBORDecode_GetMIMEInternal(uTagRequirement,
+                                                             &Item,
+                                                             pMessage,
+                                                             pbIsTag257);
+   }
+}
+
+
+static inline void
+QCBORDecode_GetBinaryUUID(QCBORDecodeContext *pMe,
+                          uint8_t             uTagRequirement,
+                          UsefulBufC         *pUUID)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_UUID, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pUUID);
+}
+
+inline static void
+QCBORDecode_GetBinaryUUIDInMapN(QCBORDecodeContext *pMe,
+                                int64_t             nLabel,
+                                uint8_t             uTagRequirement,
+                                UsefulBufC         *pUUID)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_UUID, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pUUID);
+}
+
+inline static void
+QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pMe,
+                                 const char         *szLabel,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pUUID)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_UUID, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pUUID);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* qcbor_spiffy_decode_h */
diff --git a/min_use_main.c b/min_use_main.c
deleted file mode 100644
index ba7ae67..0000000
--- a/min_use_main.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*==============================================================================
-
-Copyright (c) 2018, Laurence Lundblade.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-    * The name "Laurence Lundblade" may not be used to
-      endorse or promote products derived from this software without
-      specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-==============================================================================*/
-//  Created by Laurence Lundblade on 10/26/18.
-
-#include <stdio.h>
-#include "qcbor/qcbor.h"
-
-
-
-/*
- A small user of CBOR encoding and decoding
- that is good as an example and for
- checking code size with all the
- inlining and dead stripping on.
-
- */
-
-int main(int argc, const char * argv[])
-{
-    (void)argc; // Suppress unused warning
-    (void)argv; // Suppress unused warning
-
-    uint8_t pBuf[300];
-    // Very simple CBOR, a map with one boolean that is true in it
-    QCBOREncodeContext EC;
-
-    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(pBuf));
-
-    QCBOREncode_OpenMap(&EC);
-    QCBOREncode_AddBoolToMapN(&EC, 66, true);
-    QCBOREncode_CloseMap(&EC);
-
-    UsefulBufC Encoded;
-    if(QCBOREncode_Finish(&EC, &Encoded)) {
-        return -1;
-    }
-
-
-    // Decode it and see that is right
-    QCBORDecodeContext DC;
-    QCBORItem Item;
-    QCBORDecode_Init(&DC, Encoded, QCBOR_DECODE_MODE_NORMAL);
-
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_MAP) {
-        return -2;
-    }
-
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_TRUE) {
-        return -3;
-    }
-
-    if(QCBORDecode_Finish(&DC)) {
-        return -4;
-    }
-
-
-    // Make another encoded message with the CBOR from the previous put into this one
-    UsefulBuf_MAKE_STACK_UB(MemoryForEncoded2, 20);
-    QCBOREncode_Init(&EC, MemoryForEncoded2);
-    QCBOREncode_OpenArray(&EC);
-    QCBOREncode_AddUInt64(&EC, 451);
-    QCBOREncode_AddEncoded(&EC, Encoded);
-    QCBOREncode_OpenMap(&EC);
-    QCBOREncode_AddEncodedToMapN(&EC, -70000, Encoded);
-    QCBOREncode_CloseMap(&EC);
-    QCBOREncode_CloseArray(&EC);
-
-    UsefulBufC Encoded2;
-    if(QCBOREncode_Finish(&EC, &Encoded2)) {
-        return -5;
-    }
-    /*
-     [                // 0    1:3
-     451,          // 1    1:2
-     {             // 1    1:2   2:1
-     66: true    // 2    1:1
-     },
-     {             // 1    1:1   2:1
-     -70000: {   // 2    1:1   2:1   3:1
-     66: true  // 3    XXXXXX
-     }
-     }
-     ]
-
-
-
-     83                # array(3)
-     19 01C3        # unsigned(451)
-     A1             # map(1)
-     18 42       # unsigned(66)
-     F5          # primitive(21)
-     A1             # map(1)
-     3A 0001116F # negative(69999)
-     A1          # map(1)
-     18 42    # unsigned(66)
-     F5       # primitive(21)
-     */
-
-    // Decode it and see if it is OK
-    QCBORDecode_Init(&DC, Encoded2, QCBOR_DECODE_MODE_NORMAL);
-
-    // 0    1:3
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.val.uCount != 3) {
-        return -6;
-    }
-
-    // 1    1:2
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_INT64 || Item.val.uint64 != 451) {
-        return -7;
-    }
-
-    // 1    1:2   2:1
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 1) {
-        return -8;
-    }
-
-    // 2    1:1
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_TRUE) {
-        return -9;
-    }
-
-    // 1    1:1   2:1
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 1) {
-        return -10;
-    }
-
-    // 2    1:1   2:1   3:1
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 1 || Item.uLabelType != QCBOR_TYPE_INT64 || Item.label.int64 != -70000) {
-        return -11;
-    }
-
-    // 3    XXXXXX
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_TRUE || Item.uLabelType != QCBOR_TYPE_INT64 || Item.label.int64 != 66) {
-        return -12;
-    }
-
-    if(QCBORDecode_Finish(&DC)) {
-        return -13;
-    }
-
-    return 0;
-}
diff --git a/src/ieee754.c b/src/ieee754.c
index 8b2efa8..216cd00 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -400,74 +400,11 @@
 }
 
 
-// Public function; see ieee754.h
-double IEEE754_FloatToDouble(uint32_t uFloat)
-{
-    // Pull out the three parts of the single-precision float.  Do all
-    // the work in 64 bits because that is what the end result is.  It
-    // may give smaller code size and will keep static analyzers
-    // happier.
-    const uint64_t uSingleSignificand      = uFloat & SINGLE_SIGNIFICAND_MASK;
-    const int64_t  nSingleUnBiasedExponent = (int64_t)((uFloat & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT) - SINGLE_EXPONENT_BIAS;
-    const uint64_t uSingleSign             = (uFloat & SINGLE_SIGN_MASK) >> SINGLE_SIGN_SHIFT;
 
-
-    // Make the three parts of hte single-precision number
-    uint64_t uDoubleSignificand, uDoubleSign, uDoubleBiasedExponent;
-    if(nSingleUnBiasedExponent == SINGLE_EXPONENT_ZERO) {
-        // 0 or subnormal
-        uDoubleBiasedExponent = DOUBLE_EXPONENT_ZERO + DOUBLE_EXPONENT_BIAS;
-        if(uSingleSignificand) {
-            // Subnormal case
-            uDoubleBiasedExponent = -SINGLE_EXPONENT_BIAS + DOUBLE_EXPONENT_BIAS + 1;
-            // A single-precision subnormal can always be converted to
-            // a normal double-precision float because the ranges line
-            // up
-            uDoubleSignificand = uSingleSignificand;
-            // Shift bits from right of the decimal to left, reducing
-            // the exponent by 1 each time
-            do {
-                uDoubleSignificand <<= 1;
-                uDoubleBiasedExponent--;
-                // TODO: is this right? Where does 0x400 come from?
-            } while ((uDoubleSignificand & 0x400) == 0);
-            uDoubleSignificand &= SINGLE_SIGNIFICAND_MASK;
-            uDoubleSignificand <<= (DOUBLE_NUM_SIGNIFICAND_BITS - SINGLE_NUM_SIGNIFICAND_BITS);
-        } else {
-            // Just zero
-            uDoubleSignificand = 0;
-        }
-    } else if(nSingleUnBiasedExponent == SINGLE_EXPONENT_INF_OR_NAN) {
-        // NaN or Inifinity
-        uDoubleBiasedExponent = DOUBLE_EXPONENT_INF_OR_NAN + DOUBLE_EXPONENT_BIAS;
-        if(uSingleSignificand) {
-            // NaN
-            // First preserve the NaN payload from half to single
-            // TODO: check this
-            uDoubleSignificand = uSingleSignificand & ~SINGLE_QUIET_NAN_BIT;
-            if(uSingleSignificand & SINGLE_QUIET_NAN_BIT) {
-                // Next, set qNaN if needed since half qNaN bit is not copied above
-                uDoubleSignificand |= DOUBLE_QUIET_NAN_BIT;
-            }
-        } else {
-            // Infinity
-            uDoubleSignificand = 0;
-        }
-    } else {
-        // Normal number
-        uDoubleBiasedExponent = (uint64_t)(nSingleUnBiasedExponent + DOUBLE_EXPONENT_BIAS);
-        uDoubleSignificand    = uSingleSignificand << (DOUBLE_NUM_SIGNIFICAND_BITS - SINGLE_NUM_SIGNIFICAND_BITS);
-    }
-    uDoubleSign = uSingleSign;
-
-
-    // Shift the 3 parts into place as a double-precision
-    const uint64_t uDouble = uDoubleSignificand |
-                            (uDoubleBiasedExponent << DOUBLE_EXPONENT_SHIFT) |
-                            (uDoubleSign << DOUBLE_SIGN_SHIFT);
-    return CopyUint64ToDouble(uDouble);
-}
-
+/*
+ IEEE754_FloatToDouble(uint32_t uFloat) was created but is not needed. It can be retrieved from
+github history if needed.
+*/
 
 
 
diff --git a/src/ieee754.h b/src/ieee754.h
index d614825..d37532a 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -91,14 +91,6 @@
 double IEEE754_HalfToDouble(uint16_t uHalfPrecision);
 
 
-/*
- Convert float to double-precision without using any
- floating-point HW or compiler-supplied SW.
- This is a loss-less conversion.
- */
-double IEEE754_FloatToDouble(uint32_t ufloat);
-
-
 // Both tags the value and gives the size
 #define IEEE754_UNION_IS_HALF   2
 #define IEEE754_UNION_IS_SINGLE 4
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index e1820b9..3e36e5a 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -32,7 +32,14 @@
 
 
 #include "qcbor/qcbor_decode.h"
-#include "ieee754.h"
+#include "qcbor/qcbor_spiffy_decode.h"
+#include "ieee754.h" // Does not use math.h
+
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#include <math.h> // For isnan(), llround(), llroudf(), round(), roundf(),
+                  // pow(), exp2()
+#include <fenv.h> // feclearexcept(), fetestexcept()
+#endif
 
 
 /*
@@ -41,102 +48,304 @@
  */
 #define UNCONST_POINTER(ptr)    ((void *)(ptr))
 
+#define SIZEOF_C_ARRAY(array,type) (sizeof(array)/sizeof(type))
+
+
+static inline bool
+QCBORItem_IsMapOrArray(const QCBORItem *pMe)
+{
+   const uint8_t uDataType = pMe->uDataType;
+   return uDataType == QCBOR_TYPE_MAP ||
+          uDataType == QCBOR_TYPE_ARRAY ||
+          uDataType == QCBOR_TYPE_MAP_AS_ARRAY;
+}
+
+static inline bool
+QCBORItem_IsEmptyDefiniteLengthMapOrArray(const QCBORItem *pMe)
+{
+   if(!QCBORItem_IsMapOrArray(pMe)){
+      return false;
+   }
+
+   if(pMe->val.uCount != 0) {
+      return false;
+   }
+   return true;
+}
+
+static inline bool
+QCBORItem_IsIndefiniteLengthMapOrArray(const QCBORItem *pMe)
+{
+   if(!QCBORItem_IsMapOrArray(pMe)){
+      return false;
+   }
+
+   if(pMe->val.uCount != QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH) {
+      return false;
+   }
+   return true;
+}
 
 
 /*===========================================================================
- DecodeNesting -- Functions for tracking array/map nesting when decoding
-
- See qcbor/qcbor_decode.h for definition of the object
-  used here: QCBORDecodeNesting
+   DecodeNesting -- Tracking array/map/sequence/bstr-wrapped nesting
   ===========================================================================*/
 
-inline static int
-IsMapOrArray(uint8_t uDataType)
+/*
+ See comments about and typedef of QCBORDecodeNesting in qcbor_private.h,
+ the data structure all these functions work on.
+ */
+
+
+static inline uint8_t
+DecodeNesting_GetCurrentLevel(const QCBORDecodeNesting *pNesting)
 {
-   return uDataType == QCBOR_TYPE_MAP || uDataType == QCBOR_TYPE_ARRAY;
+   const ptrdiff_t nLevel = pNesting->pCurrent - &(pNesting->pLevels[0]);
+   /*
+    Limit in DecodeNesting_Descend against more than
+    QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe
+    */
+   return (uint8_t)nLevel;
 }
 
-inline static int
-DecodeNesting_IsNested(const QCBORDecodeNesting *pNesting)
+
+static inline uint8_t
+DecodeNesting_GetBoundedModeLevel(const QCBORDecodeNesting *pNesting)
 {
-   return pNesting->pCurrent != &(pNesting->pMapsAndArrays[0]);
+   const ptrdiff_t nLevel = pNesting->pCurrentBounded - &(pNesting->pLevels[0]);
+   /*
+    Limit in DecodeNesting_Descend against more than
+    QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe
+    */
+   return (uint8_t)nLevel;
 }
 
-inline static int
-DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
+
+static inline uint32_t
+DecodeNesting_GetMapOrArrayStart(const QCBORDecodeNesting *pNesting)
 {
-   return pNesting->pCurrent->uCount == UINT16_MAX;
+   return pNesting->pCurrentBounded->u.ma.uStartOffset;
 }
 
-inline static uint8_t
-DecodeNesting_GetLevel(QCBORDecodeNesting *pNesting)
+
+static inline bool
+DecodeNesting_IsBoundedEmpty(const QCBORDecodeNesting *pNesting)
 {
-   // Check in DecodeNesting_Descend and never having
-   // QCBOR_MAX_ARRAY_NESTING > 255 gaurantee cast is safe
-   return (uint8_t)(pNesting->pCurrent - &(pNesting->pMapsAndArrays[0]));
+   if(pNesting->pCurrentBounded->u.ma.uCountCursor == QCBOR_COUNT_INDICATES_ZERO_LENGTH) {
+      return true;
+   } else {
+      return false;
+   }
 }
 
-inline static int
-DecodeNesting_TypeIsMap(const QCBORDecodeNesting *pNesting)
+
+static inline bool
+DecodeNesting_IsCurrentAtTop(const QCBORDecodeNesting *pNesting)
 {
-   if(!DecodeNesting_IsNested(pNesting)) {
-      return 0;
+   if(pNesting->pCurrent == &(pNesting->pLevels[0])) {
+      return true;
+   } else {
+      return false;
+   }
+}
+
+
+static inline bool
+DecodeNesting_IsCurrentDefiniteLength(const QCBORDecodeNesting *pNesting)
+{
+   if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
+      // Not a map or array
+      return false;
+   }
+   if(pNesting->pCurrent->u.ma.uCountTotal == QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH) {
+      // Is indefinite
+      return false;
+   }
+   // All checks passed; is a definte length map or array
+   return true;
+}
+
+
+static inline bool
+DecodeNesting_IsCurrentBstrWrapped(const QCBORDecodeNesting *pNesting)
+{
+   if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
+      // is a byte string
+      return true;
+   }
+   return false;
+}
+
+
+static inline bool DecodeNesting_IsCurrentBounded(const QCBORDecodeNesting *pNesting)
+{
+   if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
+      return true;
+   }
+   if(pNesting->pCurrent->u.ma.uStartOffset != QCBOR_NON_BOUNDED_OFFSET) {
+      return true;
+   }
+   return false;
+}
+
+
+static inline void DecodeNesting_SetMapOrArrayBoundedMode(QCBORDecodeNesting *pNesting, bool bIsEmpty, size_t uStart)
+{
+   // Should be only called on maps and arrays
+   /*
+    DecodeNesting_EnterBoundedMode() checks to be sure uStart is not
+    larger than DecodeNesting_EnterBoundedMode which keeps it less than
+    uin32_t so the cast is safe.
+    */
+   pNesting->pCurrent->u.ma.uStartOffset = (uint32_t)uStart;
+
+   if(bIsEmpty) {
+      pNesting->pCurrent->u.ma.uCountCursor = QCBOR_COUNT_INDICATES_ZERO_LENGTH;
+   }
+}
+
+
+static inline void DecodeNesting_ClearBoundedMode(QCBORDecodeNesting *pNesting)
+{
+   pNesting->pCurrent->u.ma.uStartOffset = QCBOR_NON_BOUNDED_OFFSET;
+}
+
+
+static inline bool
+DecodeNesting_IsAtEndOfBoundedLevel(const QCBORDecodeNesting *pNesting)
+{
+   if(pNesting->pCurrentBounded == NULL) {
+      // No bounded map or array set up
+      return false;
+   }
+   if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
+      // Not a map or array; end of those is by byte count
+      return false;
+   }
+   if(!DecodeNesting_IsCurrentBounded(pNesting)) {
+      // In a traveral at a level deeper than the bounded level
+      return false;
+   }
+   // Works for both definite and indefinite length maps/arrays
+   if(pNesting->pCurrentBounded->u.ma.uCountCursor != 0) {
+      // Count is not zero, still unconsumed item
+      return false;
+   }
+   // All checks passed, got to the end of an array or map
+   return true;
+}
+
+
+static inline bool
+DecodeNesting_IsEndOfDefiniteLengthMapOrArray(const QCBORDecodeNesting *pNesting)
+{
+   // Must only be called on map / array
+   if(pNesting->pCurrent->u.ma.uCountCursor == 0) {
+      return true;
+   } else {
+      return false;
+   }
+}
+
+
+static inline bool
+DecodeNesting_IsCurrentTypeMap(const QCBORDecodeNesting *pNesting)
+{
+   if(pNesting->pCurrent->uLevelType == CBOR_MAJOR_TYPE_MAP) {
+      return true;
+   } else {
+      return false;
+   }
+}
+
+
+static inline bool
+DecodeNesting_IsBoundedType(const QCBORDecodeNesting *pNesting, uint8_t uType)
+{
+   if(pNesting->pCurrentBounded == NULL) {
+      return false;
    }
 
-   return CBOR_MAJOR_TYPE_MAP == pNesting->pCurrent->uMajorType;
+   if(pNesting->pCurrentBounded->uLevelType != uType) {
+      return false;
+   }
+
+   return true;
 }
 
-// Process a break. This will either ascend the nesting or error out
-inline static QCBORError
-DecodeNesting_BreakAscend(QCBORDecodeNesting *pNesting)
+
+static inline void
+DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(QCBORDecodeNesting *pNesting)
 {
-   // breaks must always occur when there is nesting
-   if(!DecodeNesting_IsNested(pNesting)) {
-      return QCBOR_ERR_BAD_BREAK;
-   }
+   // Only call on a defnite length array / map
+   pNesting->pCurrent->u.ma.uCountCursor--;
+}
 
-   // breaks can only occur when the map/array is indefinite length
-   if(!DecodeNesting_IsIndefiniteLength(pNesting)) {
-      return QCBOR_ERR_BAD_BREAK;
-   }
 
-   // if all OK, the break reduces the level of nesting
+static inline void
+DecodeNesting_ReverseDecrement(QCBORDecodeNesting *pNesting)
+{
+   // Only call on a defnite length array / map
+   pNesting->pCurrent->u.ma.uCountCursor++;
+}
+
+
+static inline void
+DecodeNesting_Ascend(QCBORDecodeNesting *pNesting)
+{
    pNesting->pCurrent--;
+}
+
+
+static QCBORError
+DecodeNesting_Descend(QCBORDecodeNesting *pNesting, uint8_t uType)
+{
+   // Error out if nesting is too deep
+   if(pNesting->pCurrent >= &(pNesting->pLevels[QCBOR_MAX_ARRAY_NESTING])) {
+      return QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP;
+   }
+
+   // The actual descend
+   pNesting->pCurrent++;
+
+   pNesting->pCurrent->uLevelType = uType;
 
    return QCBOR_SUCCESS;
 }
 
-// Called on every single item except breaks including open of a map/array
-inline static void
-DecodeNesting_DecrementCount(QCBORDecodeNesting *pNesting)
+
+static inline QCBORError
+DecodeNesting_EnterBoundedMapOrArray(QCBORDecodeNesting *pNesting, bool bIsEmpty, size_t uOffset)
 {
-   while(DecodeNesting_IsNested(pNesting)) {
-      // Not at the top level, so there is decrementing to be done.
+   /*
+    Should only be called on map/array.
 
-      if(!DecodeNesting_IsIndefiniteLength(pNesting)) {
-         // Decrement the current nesting level if it is not indefinite.
-         pNesting->pCurrent->uCount--;
-      }
+    Have descended into this before this is called. The job here is
+    just to mark it in bounded mode.
 
-      if(pNesting->pCurrent->uCount != 0) {
-         // Did not close out an array or map, so nothing further
-         break;
-      }
-
-      // Closed out an array or map so level up
-      pNesting->pCurrent--;
-
-      // Continue with loop to see if closing out this doesn't close out more
+    Check against QCBOR_MAX_DECODE_INPUT_SIZE make sure that
+    uOffset doesn't collide with QCBOR_NON_BOUNDED_OFFSET
+    */
+   if(uOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) {
+      return QCBOR_ERR_INPUT_TOO_LARGE;
    }
+
+   pNesting->pCurrentBounded = pNesting->pCurrent;
+
+   DecodeNesting_SetMapOrArrayBoundedMode(pNesting, bIsEmpty, uOffset);
+
+   return QCBOR_SUCCESS;
 }
 
-// Called on every map/array
-inline static QCBORError
-DecodeNesting_Descend(QCBORDecodeNesting *pNesting, QCBORItem *pItem)
-{
-   QCBORError nReturn = QCBOR_SUCCESS;
 
-   if(pItem->val.uCount == 0) {
+static inline QCBORError
+DecodeNesting_DescendMapOrArray(QCBORDecodeNesting *pNesting,
+                                uint8_t             uQCBORType,
+                                uint64_t            uCount)
+{
+   QCBORError uError = QCBOR_SUCCESS;
+
+   if(uCount == 0) {
       // Nothing to do for empty definite lenth arrays. They are just are
       // effectively the same as an item that is not a map or array
       goto Done;
@@ -144,166 +353,120 @@
    }
 
    // Error out if arrays is too long to handle
-   if(pItem->val.uCount != UINT16_MAX && pItem->val.uCount > QCBOR_MAX_ITEMS_IN_ARRAY) {
-      nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+   if(uCount != QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH &&
+      uCount > QCBOR_MAX_ITEMS_IN_ARRAY) {
+      uError = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
       goto Done;
    }
 
-   // Error out if nesting is too deep
-   if(pNesting->pCurrent >= &(pNesting->pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING])) {
-      nReturn = QCBOR_ERR_ARRAY_NESTING_TOO_DEEP;
+   uError = DecodeNesting_Descend(pNesting, uQCBORType);
+   if(uError != QCBOR_SUCCESS) {
       goto Done;
    }
 
-   // The actual descend
-   pNesting->pCurrent++;
+   // Fill in the new map/array level. Check above makes casts OK.
+   pNesting->pCurrent->u.ma.uCountCursor  = (uint16_t)uCount;
+   pNesting->pCurrent->u.ma.uCountTotal   = (uint16_t)uCount;
 
-   // Record a few details for this nesting level
-   pNesting->pCurrent->uMajorType = pItem->uDataType;
-   pNesting->pCurrent->uCount     = pItem->val.uCount;
+   DecodeNesting_ClearBoundedMode(pNesting);
 
 Done:
-   return nReturn;;
+   return uError;;
 }
 
-inline static void
+
+static inline void
+DecodeNesting_LevelUpCurrent(QCBORDecodeNesting *pNesting)
+{
+   pNesting->pCurrent = pNesting->pCurrentBounded - 1;
+}
+
+
+static inline void
+DecodeNesting_LevelUpBounded(QCBORDecodeNesting *pNesting)
+{
+   while(pNesting->pCurrentBounded != &(pNesting->pLevels[0])) {
+      pNesting->pCurrentBounded--;
+      if(DecodeNesting_IsCurrentBounded(pNesting)) {
+         break;
+      }
+   }
+}
+
+static inline void
+DecodeNesting_SetCurrentToBoundedLevel(QCBORDecodeNesting *pNesting)
+{
+   pNesting->pCurrent = pNesting->pCurrentBounded;
+}
+
+
+static inline QCBORError
+DecodeNesting_DescendIntoBstrWrapped(QCBORDecodeNesting *pNesting,
+                                     uint32_t uEndOffset,
+                                     uint32_t uEndOfBstr)
+{
+   QCBORError uError = QCBOR_SUCCESS;
+
+   uError = DecodeNesting_Descend(pNesting, QCBOR_TYPE_BYTE_STRING);
+   if(uError != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   // Fill in the new byte string level
+   pNesting->pCurrent->u.bs.uPreviousEndOffset = uEndOffset;
+   pNesting->pCurrent->u.bs.uEndOfBstr         = uEndOfBstr;
+
+   // Bstr wrapped levels are always bounded
+   pNesting->pCurrentBounded = pNesting->pCurrent;
+
+Done:
+   return uError;;
+}
+
+
+static inline void
+DecodeNesting_ZeroMapOrArrayCount(QCBORDecodeNesting *pNesting)
+{
+   pNesting->pCurrent->u.ma.uCountCursor = 0;
+}
+
+
+static inline void
 DecodeNesting_Init(QCBORDecodeNesting *pNesting)
 {
-   pNesting->pCurrent = &(pNesting->pMapsAndArrays[0]);
+   /* Assumes that *pNesting has been zero'd before this call. */
+   pNesting->pLevels[0].uLevelType = QCBOR_TYPE_BYTE_STRING;
+   pNesting->pCurrent = &(pNesting->pLevels[0]);
 }
 
 
-
-/*
- 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_SIX
-   CBOR_TAG_DATE_EPOCH, // See TAG_MAPPER_FIRST_SIX
-   CBOR_TAG_POS_BIGNUM, // See TAG_MAPPER_FIRST_SIX
-   CBOR_TAG_NEG_BIGNUM, // See TAG_MAPPER_FIRST_SIX
-   CBOR_TAG_DECIMAL_FRACTION, // See TAG_MAPPER_FIRST_SIX
-   CBOR_TAG_BIGFLOAT, // See TAG_MAPPER_FIRST_SIX
-   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 six items in
-// spBuiltInTagMap don't have values 0,1,2,3,4,5. 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 QCBOR_TAGFLAG_DECIMAL_FRACTION (0x01LL << CBOR_TAG_DECIMAL_FRACTION)
-#define QCBOR_TAGFLAG_BIGFLOAT         (0x01LL << CBOR_TAG_BIGFLOAT)
-
-#define TAG_MAPPER_FIRST_SIX (QCBOR_TAGFLAG_DATE_STRING       |\
-                               QCBOR_TAGFLAG_DATE_EPOCH       |\
-                               QCBOR_TAGFLAG_POS_BIGNUM       |\
-                               QCBOR_TAGFLAG_NEG_BIGNUM       |\
-                               QCBOR_TAGFLAG_DECIMAL_FRACTION |\
-                               QCBOR_TAGFLAG_BIGFLOAT)
-
-#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)
+static inline void
+DecodeNesting_PrepareForMapSearch(QCBORDecodeNesting *pNesting, QCBORDecodeNesting *pSave)
 {
-   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
+   *pSave = *pNesting;
+   pNesting->pCurrent = pNesting->pCurrentBounded;
+   pNesting->pCurrent->u.ma.uCountCursor = pNesting->pCurrent->u.ma.uCountTotal;
 }
 
-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
+static inline void
+DecodeNesting_RestoreFromMapSearch(QCBORDecodeNesting *pNesting, const QCBORDecodeNesting *pSave)
+{
+   *pNesting = *pSave;
 }
 
-/*
-  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 QCBORError
-TagMapper_Lookup(const QCBORTagListIn *pCallerConfiguredTagMap,
-                 uint64_t uTag,
-                 uint8_t *puTagBitIndex)
+static inline uint32_t
+DecodeNesting_GetEndOfBstr(const QCBORDecodeNesting *pMe)
 {
-   int nTagBitIndex = TagMapper_LookupBuiltIn(uTag);
-   if(nTagBitIndex >= 0) {
-      // Cast is safe because TagMapper_LookupBuiltIn never returns > 47
-      *puTagBitIndex = (uint8_t)nTagBitIndex;
-      return QCBOR_SUCCESS;
-   }
+   return pMe->pCurrentBounded->u.bs.uEndOfBstr;
+}
 
-   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;
+static inline uint32_t
+DecodeNesting_GetPreviousBoundedEnd(const QCBORDecodeNesting *pMe)
+{
+   return pMe->pCurrentBounded->u.bs.uPreviousEndOffset;
 }
 
 
@@ -367,6 +530,9 @@
    // passed it will just act as if the default normal mode of 0 was set.
    me->uDecodeMode = (uint8_t)nDecodeMode;
    DecodeNesting_Init(&(me->nesting));
+   for(int i = 0; i < QCBOR_NUM_MAPPED_TAGS; i++) {
+      me->auMappedTags[i] = CBOR_TAG_INVALID16;
+   }
 }
 
 
@@ -387,10 +553,12 @@
 /*
  Public function, see header file
  */
-void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *me,
+void QCBORDecode_SetCallerConfiguredTagList(QCBORDecodeContext *pMe,
                                             const QCBORTagListIn *pTagList)
 {
-   me->pCallerConfiguredTagList = pTagList;
+   // This does nothing now. It is retained for backwards compatibility
+   (void)pMe;
+   (void)pTagList;
 }
 
 
@@ -398,7 +566,7 @@
  This decodes the fundamental part of a CBOR data item, the type and
  number
 
- This is the Counterpart to InsertEncodedTypeAndNumber().
+ This is the counterpart to QCBOREncode_EncodeHead().
 
  This does the network->host byte order conversion. The conversion
  here also results in the conversion for floats in addition to that
@@ -416,8 +584,12 @@
  The int type is preferred to uint8_t for some variables as this
  avoids integer promotions, can reduce code size and makes
  static analyzers happier.
+
+ @retval QCBOR_ERR_UNSUPPORTED
+
+ @retval QCBOR_ERR_HIT_END
  */
-inline static QCBORError DecodeTypeAndNumber(UsefulInputBuf *pUInBuf,
+static inline QCBORError DecodeTypeAndNumber(UsefulInputBuf *pUInBuf,
                                               int *pnMajorType,
                                               uint64_t *puArgument,
                                               int *pnAdditionalInfo)
@@ -435,8 +607,8 @@
    uint64_t uArgument;
 
    if(nAdditionalInfo >= LEN_IS_ONE_BYTE && nAdditionalInfo <= LEN_IS_EIGHT_BYTES) {
-      // Need to get 1,2,4 or 8 additional argument bytes Map
-      // LEN_IS_ONE_BYTE.. LEN_IS_EIGHT_BYTES to actual length
+      // Need to get 1,2,4 or 8 additional argument bytes. Map
+      // LEN_IS_ONE_BYTE..LEN_IS_EIGHT_BYTES to actual length
       static const uint8_t aIterate[] = {1,2,4,8};
 
       // Loop getting all the bytes in the argument
@@ -481,15 +653,17 @@
  zero than positive.  Stdint, as far as I can tell, uses two's
  compliment to represent negative integers.
 
- See http://www.unix.org/whitepapers/64bit.html for reasons int isn't
+ See http://www.unix.org/whitepapers/64bit.html for reasons int is
  used carefully here, and in particular why it isn't used in the interface.
  Also see
  https://stackoverflow.com/questions/17489857/why-is-int-typically-32-bit-on-64-bit-compilers
 
  Int is used for values that need less than 16-bits and would be subject
  to integer promotion and complaining by static analyzers.
+
+ @retval QCBOR_ERR_INT_OVERFLOW
  */
-inline static QCBORError
+static inline QCBORError
 DecodeInteger(int nMajorType, uint64_t uNumber, QCBORItem *pDecodedItem)
 {
    QCBORError nReturn = QCBOR_SUCCESS;
@@ -554,43 +728,66 @@
 
 /*
  Decode true, false, floats, break...
+
+ @retval QCBOR_ERR_HALF_PRECISION_DISABLED
+
+ @retval QCBOR_ERR_BAD_TYPE_7
  */
-inline static QCBORError
+static inline QCBORError
 DecodeSimple(int nAdditionalInfo, uint64_t uNumber, QCBORItem *pDecodedItem)
 {
    QCBORError nReturn = QCBOR_SUCCESS;
 
-   // uAdditionalInfo is 5 bits from the initial byte compile time checks
+   // uAdditionalInfo is 5 bits from the initial byte. Compile time checks
    // above make sure uAdditionalInfo values line up with uDataType values.
-   // DecodeTypeAndNumber never returns a major type > 1f so cast is safe
+   // DecodeTypeAndNumber() never returns an AdditionalInfo > 0x1f so cast
+   // is safe
    pDecodedItem->uDataType = (uint8_t)nAdditionalInfo;
 
    switch(nAdditionalInfo) {
       // No check for ADDINFO_RESERVED1 - ADDINFO_RESERVED3 as they are
       // caught before this is called.
 
-      case HALF_PREC_FLOAT:
+      case HALF_PREC_FLOAT: // 25
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+         // Half-precision is returned as a double.
          // The cast to uint16_t is safe because the encoded value
          // was 16 bits. It was widened to 64 bits to be passed in here.
          pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uNumber);
          pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
 #else
-         nReturn = QCBOR_ERR_HALF_PRECISION_UNSUPPORTED;
+         nReturn = QCBOR_ERR_HALF_PRECISION_DISABLED;
 #endif
          break;
-      case SINGLE_PREC_FLOAT:
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+      case SINGLE_PREC_FLOAT: // 26
+         // Single precision is normally returned as a double
+         // since double is widely supported, there is no loss of
+         // precision, it makes it easy for the caller in
+         // most cases and it can be converted back to single
+         // with no loss of precision
+         //
          // The cast to uint32_t is safe because the encoded value
          // was 32 bits. It was widened to 64 bits to be passed in here.
-         pDecodedItem->val.dfnum = IEEE754_FloatToDouble((uint32_t)uNumber);
-         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+         {
+            const float f = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+            // In the normal case, use HW to convert float to double.
+            pDecodedItem->val.dfnum = (double)f;
+            pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
 #else
-         pDecodedItem->val.fnum = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
-         pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
+            // Use of float HW is disabled, return as a float.
+            pDecodedItem->val.fnum = f;
+            pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
+
+            // IEEE754_FloatToDouble() could be used here to return
+            // as a double, but it adds object code and most likely
+            // anyone disabling FLOAT HW use doesn't care about
+            // floats and wants to save object code.
 #endif
+         }
          break;
-      case DOUBLE_PREC_FLOAT:
+
+      case DOUBLE_PREC_FLOAT: // 27
          pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uNumber);
          pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
          break;
@@ -630,8 +827,14 @@
 
 /*
  Decode text and byte strings. Call the string allocator if asked to.
+
+ @retval QCBOR_ERR_HIT_END
+
+ @retval QCBOR_ERR_STRING_ALLOCATE
+
+ @retval QCBOR_ERR_STRING_TOO_LONG
  */
-inline static QCBORError DecodeBytes(const QCORInternalAllocator *pAllocator,
+static inline QCBORError DecodeBytes(const QCORInternalAllocator *pAllocator,
                                      int nMajorType,
                                      uint64_t uStrLen,
                                      UsefulInputBuf *pUInBuf,
@@ -702,6 +905,21 @@
 
  Errors detected here include: an array that is too long to decode,
  hit end of buffer unexpectedly, a few forms of invalid encoded CBOR
+
+ @retval QCBOR_ERR_UNSUPPORTED
+
+ @retval QCBOR_ERR_HIT_END
+
+ @retval QCBOR_ERR_INT_OVERFLOW
+
+ @retval QCBOR_ERR_STRING_ALLOCATE
+
+ @retval QCBOR_ERR_STRING_TOO_LONG
+
+ @retval QCBOR_ERR_HALF_PRECISION_DISABLED
+
+ @retval QCBOR_ERR_BAD_TYPE_7
+
  */
 static QCBORError GetNext_Item(UsefulInputBuf *pUInBuf,
                                QCBORItem *pDecodedItem,
@@ -757,11 +975,11 @@
       case CBOR_MAJOR_TYPE_MAP:   // Major type 5
          // Record the number of items in the array or map
          if(uNumber > QCBOR_MAX_ITEMS_IN_ARRAY) {
-            nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+            nReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
             goto Done;
          }
          if(nAdditionalInfo == LEN_IS_INDEFINITE) {
-            pDecodedItem->val.uCount = UINT16_MAX; // Indicate indefinite length
+            pDecodedItem->val.uCount = QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH;
          } else {
             // type conversion OK because of check above
             pDecodedItem->val.uCount = (uint16_t)uNumber;
@@ -776,7 +994,7 @@
             nReturn = QCBOR_ERR_BAD_INT;
          } else {
             pDecodedItem->val.uTagV = uNumber;
-            pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
+            pDecodedItem->uDataType = QCBOR_TYPE_TAG;
          }
          break;
 
@@ -803,6 +1021,24 @@
  allocator.
 
  Code Reviewers: THIS FUNCTION DOES A LITTLE POINTER MATH
+
+ @retval QCBOR_ERR_UNSUPPORTED
+
+ @retval QCBOR_ERR_HIT_END
+
+ @retval QCBOR_ERR_INT_OVERFLOW
+
+ @retval QCBOR_ERR_STRING_ALLOCATE
+
+ @retval QCBOR_ERR_STRING_TOO_LONG
+
+ @retval QCBOR_ERR_HALF_PRECISION_DISABLED
+
+ @retval QCBOR_ERR_BAD_TYPE_7
+
+ @retval QCBOR_ERR_NO_STRING_ALLOCATOR
+
+ @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK
  */
 static inline QCBORError
 GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
@@ -846,7 +1082,7 @@
       goto Done;
    }
 
-   // Loop getting chunk of indefinite string
+   // Loop getting chunks of the indefinite length string
    UsefulBufC FullString = NULLUsefulBufC;
 
    for(;;) {
@@ -902,81 +1138,148 @@
    return nReturn;
 }
 
+static uint64_t ConvertTag(const QCBORDecodeContext *me, uint16_t uTagVal) {
+   if(uTagVal <= QCBOR_LAST_UNMAPPED_TAG) {
+      return uTagVal;
+   } else if(uTagVal == CBOR_TAG_INVALID16) {
+      return CBOR_TAG_INVALID64;
+   } else {
+      // This won't be negative because of code below in GetNext_TaggedItem()
+      const unsigned uIndex = uTagVal - (QCBOR_LAST_UNMAPPED_TAG + 1);
+      return me->auMappedTags[uIndex];
+   }
+}
+
 
 /*
  Gets all optional tag data items preceding a data item that is not an
  optional tag and records them as bits in the tag map.
+
+ @retval QCBOR_ERR_UNSUPPORTED
+
+ @retval QCBOR_ERR_HIT_END
+
+ @retval QCBOR_ERR_INT_OVERFLOW
+
+ @retval QCBOR_ERR_STRING_ALLOCATE
+
+ @retval QCBOR_ERR_STRING_TOO_LONG
+
+ @retval QCBOR_ERR_HALF_PRECISION_DISABLED
+
+ @retval QCBOR_ERR_BAD_TYPE_7
+
+ @retval QCBOR_ERR_NO_STRING_ALLOCATOR
+
+ @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK
+
+ @retval QCBOR_ERR_TOO_MANY_TAGS
  */
 static QCBORError
-GetNext_TaggedItem(QCBORDecodeContext *me,
-                   QCBORItem *pDecodedItem,
-                   QCBORTagListOut *pTags)
+GetNext_TaggedItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
 {
-   // Stack usage: int/ptr: 3 -- 24
-   QCBORError nReturn;
-   uint64_t  uTagBits = 0;
-   if(pTags) {
-      pTags->uNumUsed = 0;
-   }
+   uint16_t auTags[QCBOR_MAX_TAGS_PER_ITEM] = {CBOR_TAG_INVALID16,
+                                               CBOR_TAG_INVALID16,
+                                               CBOR_TAG_INVALID16,
+                                               CBOR_TAG_INVALID16};
+
+   QCBORError uReturn = QCBOR_SUCCESS;
 
    // Loop fetching items until the item fetched is not a tag
    for(;;) {
-      nReturn = GetNext_FullItem(me, pDecodedItem);
-      if(nReturn) {
+      QCBORError uErr = GetNext_FullItem(me, pDecodedItem);
+      if(uErr != QCBOR_SUCCESS) {
+         uReturn = uErr;
          goto Done; // Error out of the loop
       }
 
-      if(pDecodedItem->uDataType != QCBOR_TYPE_OPTTAG) {
+      if(pDecodedItem->uDataType != QCBOR_TYPE_TAG) {
          // Successful exit from loop; maybe got some tags, maybe not
-         pDecodedItem->uTagBits = uTagBits;
+         memcpy(pDecodedItem->uTags, auTags, sizeof(auTags));
          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;
+      if(auTags[QCBOR_MAX_TAGS_PER_ITEM - 1] != CBOR_TAG_INVALID16) {
+         // No room in the tag list
+         uReturn = QCBOR_ERR_TOO_MANY_TAGS;
+         // Continue on to get all tags on this item even though
+         // it is erroring out in the end. This is a 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
+      for(size_t uTagIndex = QCBOR_MAX_TAGS_PER_ITEM - 1; uTagIndex > 0; uTagIndex--) {
+         auTags[uTagIndex] = auTags[uTagIndex-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;
+      // Is the tag > 16 bits?
+      if(pDecodedItem->val.uTagV > QCBOR_LAST_UNMAPPED_TAG) {
+         size_t uTagMapIndex;
+         // Is there room in the tag map, or is it in it already?
+         for(uTagMapIndex = 0; uTagMapIndex < QCBOR_NUM_MAPPED_TAGS; uTagMapIndex++) {
+            if(me->auMappedTags[uTagMapIndex] == CBOR_TAG_INVALID16) {
+               break;
+            }
+            if(me->auMappedTags[uTagMapIndex] == pDecodedItem->val.uTagV) {
+               break;
+            }
          }
-         pTags->puTags[pTags->uNumUsed] = pDecodedItem->val.uTagV;
-         pTags->uNumUsed++;
+         if(uTagMapIndex >= QCBOR_NUM_MAPPED_TAGS) {
+            // No room for the tag
+            uReturn = QCBOR_ERR_TOO_MANY_TAGS;
+            // Continue on to get all tags on this item even though
+            // it is erroring out in the end. This is a resource limit
+            // error, not a problem with being well-formed CBOR.
+            continue;
+         }
+
+         // Covers the cases where tag is new and were it is already in the map
+         me->auMappedTags[uTagMapIndex] = pDecodedItem->val.uTagV;
+         auTags[0] = (uint16_t)(uTagMapIndex + QCBOR_LAST_UNMAPPED_TAG + 1);
+
+      } else {
+         auTags[0] = (uint16_t)pDecodedItem->val.uTagV;
       }
    }
 
 Done:
-   return nReturn;
+   return uReturn;
 }
 
 
 /*
  This layer takes care of map entries. It combines the label and data
  items into one QCBORItem.
+
+ @retval QCBOR_ERR_UNSUPPORTED
+
+ @retval QCBOR_ERR_HIT_END
+
+ @retval QCBOR_ERR_INT_OVERFLOW
+
+ @retval QCBOR_ERR_STRING_ALLOCATE
+
+ @retval QCBOR_ERR_STRING_TOO_LONG
+
+ @retval QCBOR_ERR_HALF_PRECISION_DISABLED
+
+ @retval QCBOR_ERR_BAD_TYPE_7
+
+ @retval QCBOR_ERR_NO_STRING_ALLOCATOR
+
+ @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK
+
+ @retval QCBOR_ERR_TOO_MANY_TAGS
+
+ @retval QCBOR_ERR_MAP_LABEL_TYPE
+
+ @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG
  */
 static inline QCBORError
-GetNext_MapEntry(QCBORDecodeContext *me,
-                 QCBORItem *pDecodedItem,
-                 QCBORTagListOut *pTags)
+GetNext_MapEntry(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
 {
    // Stack use: int/ptr 1, QCBORItem  -- 56
-   QCBORError nReturn = GetNext_TaggedItem(me, pDecodedItem, pTags);
+   QCBORError nReturn = GetNext_TaggedItem(me, pDecodedItem);
    if(nReturn)
       goto Done;
 
@@ -988,15 +1291,16 @@
    if(me->uDecodeMode != QCBOR_DECODE_MODE_MAP_AS_ARRAY) {
       // In a map and caller wants maps decoded, not treated as arrays
 
-      if(DecodeNesting_TypeIsMap(&(me->nesting))) {
+      if(DecodeNesting_IsCurrentTypeMap(&(me->nesting))) {
          // If in a map and the right decoding mode, get the label
 
          // Save label in pDecodedItem and get the next which will
          // be the real data
          QCBORItem LabelItem = *pDecodedItem;
-         nReturn = GetNext_TaggedItem(me, pDecodedItem, pTags);
-         if(nReturn)
+         nReturn = GetNext_TaggedItem(me, pDecodedItem);
+         if(QCBORDecode_IsUnrecoverableError(nReturn)) {
             goto Done;
+         }
 
          pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
 
@@ -1029,7 +1333,7 @@
    } else {
       if(pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
          if(pDecodedItem->val.uCount > QCBOR_MAX_ITEMS_IN_ARRAY/2) {
-            nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+            nReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG;
             goto Done;
          }
          // Decoding a map as an array
@@ -1046,111 +1350,278 @@
 
 
 /*
- Public function, see header qcbor/qcbor_decode.h file
- */
-QCBORError QCBORDecode_GetNextMapOrArray(QCBORDecodeContext *me,
-                                         QCBORItem *pDecodedItem,
-                                         QCBORTagListOut *pTags)
+ See if next item is a CBOR break. If it is, it is consumed,
+ if not it is not consumed.
+*/
+static inline QCBORError
+NextIsBreak(UsefulInputBuf *pUIB, bool *pbNextIsBreak)
 {
-   // 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.
-   QCBORError nReturn;
-
-   // Check if there are an
-   if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) == 0 && !DecodeNesting_IsNested(&(me->nesting))) {
-      nReturn = QCBOR_ERR_NO_MORE_ITEMS;
-      goto Done;
-   }
-
-   nReturn = GetNext_MapEntry(me, pDecodedItem, pTags);
-   if(nReturn) {
-      goto Done;
-   }
-
-   // Break ending arrays/maps are always processed at the end of this function.
-   // They should never show up here.
-   if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
-      nReturn = QCBOR_ERR_BAD_BREAK;
-      goto Done;
-   }
-
-   // Record the nesting level for this data item before processing any of
-   // decrementing and descending.
-   pDecodedItem->uNestingLevel = DecodeNesting_GetLevel(&(me->nesting));
-
-   // Process the item just received for descent or decrement, and
-   // ascent if decrements are enough to close out a definite length array/map
-   if(IsMapOrArray(pDecodedItem->uDataType)) {
-      // If the new item is array or map, the nesting level descends
-      nReturn = DecodeNesting_Descend(&(me->nesting), pDecodedItem);
-      // Maps and arrays do count in as items in the map/array that encloses
-      // them so a decrement needs to be done for them too, but that is done
-      // only when all the items in them have been processed, not when they
-      // are opened with the exception of an empty map or array.
-       if(pDecodedItem->val.uCount == 0) {
-           DecodeNesting_DecrementCount(&(me->nesting));
-       }
-   } else {
-      // Decrement the count of items in the enclosing map/array
-      // If the count in the enclosing map/array goes to zero, that
-      // triggers a decrement in the map/array above that and
-      // an ascend in nesting level.
-      DecodeNesting_DecrementCount(&(me->nesting));
-   }
-   if(nReturn) {
-      goto Done;
-   }
-
-   // For indefinite length maps/arrays, looking at any and
-   // all breaks that might terminate them. The equivalent
-   // for definite length maps/arrays happens in
-   // DecodeNesting_DecrementCount().
-   if(DecodeNesting_IsNested(&(me->nesting)) && DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
-      while(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
-         // Peek forward one item to see if it is a break.
-         QCBORItem Peek;
-         size_t uPeek = UsefulInputBuf_Tell(&(me->InBuf));
-         nReturn = GetNext_Item(&(me->InBuf), &Peek, NULL);
-         if(nReturn) {
-            goto Done;
-         }
-         if(Peek.uDataType != QCBOR_TYPE_BREAK) {
-            // It is not a break, rewind so it can be processed normally.
-            UsefulInputBuf_Seek(&(me->InBuf), uPeek);
-            break;
-         }
-         // It is a break. Ascend one nesting level.
-         // The break is consumed.
-         nReturn = DecodeNesting_BreakAscend(&(me->nesting));
-         if(nReturn) {
-            // break occured outside of an indefinite length array/map
-            goto Done;
-         }
+   *pbNextIsBreak = false;
+   if(UsefulInputBuf_BytesUnconsumed(pUIB) != 0) {
+      QCBORItem Peek;
+      size_t uPeek = UsefulInputBuf_Tell(pUIB);
+      QCBORError uReturn = GetNext_Item(pUIB, &Peek, NULL);
+      if(uReturn != QCBOR_SUCCESS) {
+         return uReturn;
+      }
+      if(Peek.uDataType != QCBOR_TYPE_BREAK) {
+         // It is not a break, rewind so it can be processed normally.
+         UsefulInputBuf_Seek(pUIB, uPeek);
+      } else {
+         *pbNextIsBreak = true;
       }
    }
 
-   // Tell the caller what level is next. This tells them what maps/arrays
-   // were closed out and makes it possible for them to reconstruct
-   // the tree with just the information returned by GetNext
-   pDecodedItem->uNextNestLevel = DecodeNesting_GetLevel(&(me->nesting));
+   return QCBOR_SUCCESS;
+}
+
+
+/*
+ An item was just consumed, now figure out if it was the
+ end of an array or map that can be closed out. That
+ may in turn close out another map or array.
+*/
+static QCBORError NestLevelAscender(QCBORDecodeContext *pMe, bool bMarkEnd)
+{
+   QCBORError uReturn;
+
+   /* This loops ascending nesting levels as long as there is ascending to do */
+   while(!DecodeNesting_IsCurrentAtTop(&(pMe->nesting))) {
+
+      if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) {
+         /* Decrement count for definite length maps / arrays */
+         DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(&(pMe->nesting));
+         if(!DecodeNesting_IsEndOfDefiniteLengthMapOrArray(&(pMe->nesting))) {
+             /* Didn't close out map or array, so all work here is done */
+             break;
+          }
+          /* All of a definite length array was consumed; fall through to
+             ascend */
+
+      } else {
+         /* If not definite length, have to check for a CBOR break */
+         bool bIsBreak = false;
+         uReturn = NextIsBreak(&(pMe->InBuf), &bIsBreak);
+         if(uReturn != QCBOR_SUCCESS) {
+            goto Done;
+         }
+
+         if(!bIsBreak) {
+            /* It's not a break so nothing closes out and all work is done */
+            break;
+         }
+
+         if(DecodeNesting_IsCurrentBstrWrapped(&(pMe->nesting))) {
+            /*
+             Break occurred inside a bstr-wrapped CBOR or
+             in the top level sequence. This is always an
+             error because neither are an indefinte length
+             map/array.
+             */
+            uReturn = QCBOR_ERR_BAD_BREAK;
+            goto Done;
+         }
+
+         /* It was a break in an indefinite length map / array */
+      }
+
+      /* All items in the map/array level have been consumed. */
+
+      /* But ascent in bounded mode is only by explicit call to
+         QCBORDecode_ExitBoundedMode() */
+      if(DecodeNesting_IsCurrentBounded(&(pMe->nesting))) {
+         /* Set the count to zero for definite length arrays to indicate
+            cursor is at end of bounded map / array */
+         if(bMarkEnd) {
+            // Used for definite and indefinite to signal end
+            DecodeNesting_ZeroMapOrArrayCount(&(pMe->nesting));
+
+         }
+         break;
+      }
+
+      /* Finally, actually ascend one level. */
+      DecodeNesting_Ascend(&(pMe->nesting));
+   }
+
+   uReturn = QCBOR_SUCCESS;
 
 Done:
-   if(nReturn != QCBOR_SUCCESS) {
-      // Make sure uDataType and uLabelType are QCBOR_TYPE_NONE
-      memset(pDecodedItem, 0, sizeof(QCBORItem));
+   return uReturn;
+}
+
+
+/*
+ This handles the traversal descending into and asecnding out of maps,
+ arrays and bstr-wrapped CBOR. It figures out the ends of definite and
+ indefinte length maps and arrays by looking at the item count or
+ finding CBOR breaks.  It detects the ends of the top-level sequence
+ and of bstr-wrapped CBOR by byte count.
+
+ @retval QCBOR_ERR_UNSUPPORTED X
+
+ @retval QCBOR_ERR_HIT_END
+
+ @retval QCBOR_ERR_INT_OVERFLOW X
+
+ @retval QCBOR_ERR_STRING_ALLOCATE
+
+ @retval QCBOR_ERR_STRING_TOO_LONG
+
+ @retval QCBOR_ERR_HALF_PRECISION_DISABLED X
+
+ @retval QCBOR_ERR_BAD_TYPE_7 X
+
+ @retval QCBOR_ERR_NO_STRING_ALLOCATOR
+
+ @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK
+
+ @retval QCBOR_ERR_TOO_MANY_TAGS
+
+ @retval QCBOR_ERR_MAP_LABEL_TYPE X
+
+ @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG
+
+ @retval QCBOR_ERR_NO_MORE_ITEMS
+
+ @retval QCBOR_ERR_BAD_BREAK
+
+ */
+static QCBORError
+QCBORDecode_GetNextMapOrArray(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+   QCBORError uReturn;
+   /* ==== First: figure out if at the end of a traversal ==== */
+
+   /*
+    If out of bytes to consume, it is either the end of the top-level
+    sequence of some bstr-wrapped CBOR that was entered.
+
+    In the case of bstr-wrapped CBOR, the length of the UsefulInputBuf
+    was set to that of the bstr-wrapped CBOR. When the bstr-wrapped
+    CBOR is exited, the length is set back to the top-level's length
+    or to the next highest bstr-wrapped CBOR.
+   */
+   if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) == 0) {
+      uReturn = QCBOR_ERR_NO_MORE_ITEMS;
+      goto Done;
    }
-   return nReturn;
+
+   /*
+    Check to see if at the end of a bounded definite length map or
+    array. The check for the end of an indefinite length array is
+    later.
+    */
+   if(DecodeNesting_IsAtEndOfBoundedLevel(&(me->nesting))) {
+      uReturn = QCBOR_ERR_NO_MORE_ITEMS;
+      goto Done;
+   }
+
+   /* ==== Next: not at the end so get another item ==== */
+   uReturn = GetNext_MapEntry(me, pDecodedItem);
+   if(QCBORDecode_IsUnrecoverableError(uReturn)) {
+      /* Error is so bad that traversal is not possible. */
+      goto Done;
+   }
+
+   /*
+    Breaks ending arrays/maps are always processed at the end of this
+    function. They should never show up here.
+    */
+   if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+      uReturn = QCBOR_ERR_BAD_BREAK;
+      goto Done;
+   }
+
+   /*
+     Record the nesting level for this data item before processing any
+     of decrementing and descending.
+    */
+   pDecodedItem->uNestingLevel = DecodeNesting_GetCurrentLevel(&(me->nesting));
+
+
+   /* ==== Next: Process the item for descent, ascent, decrement... ==== */
+   if(QCBORItem_IsMapOrArray(pDecodedItem)) {
+      /*
+       If the new item is a map or array, descend.
+
+       Empty indefinite length maps and arrays are descended into, but
+       then ascended out of in the next chunk of code.
+
+       Maps and arrays do count as items in the map/array that
+       encloses them so a decrement needs to be done for them too, but
+       that is done only when all the items in them have been
+       processed, not when they are opened with the exception of an
+       empty map or array.
+       */
+      QCBORError uDescendErr;
+      uDescendErr = DecodeNesting_DescendMapOrArray(&(me->nesting),
+                                                pDecodedItem->uDataType,
+                                                pDecodedItem->val.uCount);
+      if(uDescendErr != QCBOR_SUCCESS) {
+         /* This error is probably a traversal error and it
+          overrides the non-traversal error. */
+         uReturn = uDescendErr;
+         goto Done;
+      }
+   }
+
+   if(!QCBORItem_IsMapOrArray(pDecodedItem) ||
+       QCBORItem_IsEmptyDefiniteLengthMapOrArray(pDecodedItem) ||
+       QCBORItem_IsIndefiniteLengthMapOrArray(pDecodedItem)) {
+      /*
+       The following cases are handled here:
+         - A non-aggregate like an integer or string
+         - An empty definite length map or array
+         - An indefinite length map or array that might be empty or might not.
+
+       NestLevelAscender() does the work of decrementing the count for an
+       definite length map/array and break detection for an indefinite
+       length map/array. If the end of the map/array was reached, then
+       it ascends nesting levels, possibly all the way to the top level.
+       */
+      QCBORError uAscendErr;
+      uAscendErr = NestLevelAscender(me, true);
+      if(uAscendErr != QCBOR_SUCCESS) {
+         /* This error is probably a traversal error and it
+          overrides the non-traversal error. */
+         uReturn = uAscendErr;
+         goto Done;
+      }
+   }
+
+   /* ==== Last: tell the caller the nest level of the next item ==== */
+   /*
+    Tell the caller what level is next. This tells them what
+    maps/arrays were closed out and makes it possible for them to
+    reconstruct the tree with just the information returned in
+    a QCBORItem.
+   */
+   if(DecodeNesting_IsAtEndOfBoundedLevel(&(me->nesting))) {
+      /* At end of a bounded map/array; uNextNestLevel 0 to indicate this */
+      pDecodedItem->uNextNestLevel = 0;
+   } else {
+      pDecodedItem->uNextNestLevel = DecodeNesting_GetCurrentLevel(&(me->nesting));
+   }
+
+Done:
+   return uReturn;
+}
+
+static void ShiftTags(QCBORItem *pDecodedItem)
+{
+   pDecodedItem->uTags[0] = pDecodedItem->uTags[1];
+   pDecodedItem->uTags[1] = pDecodedItem->uTags[2];
+   pDecodedItem->uTags[2] = pDecodedItem->uTags[3];
+   pDecodedItem->uTags[2] = CBOR_TAG_INVALID16;
 }
 
 
 /*
  Mostly just assign the right data type for the date string.
  */
-inline static QCBORError DecodeDateString(QCBORItem *pDecodedItem)
+static inline QCBORError DecodeDateString(QCBORItem *pDecodedItem)
 {
-   // Stack Use: UsefulBuf 1 16
    if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
       return QCBOR_ERR_BAD_OPT_TAG;
    }
@@ -1158,27 +1629,11 @@
    const UsefulBufC Temp        = pDecodedItem->val.string;
    pDecodedItem->val.dateString = Temp;
    pDecodedItem->uDataType      = QCBOR_TYPE_DATE_STRING;
+   ShiftTags(pDecodedItem);
    return QCBOR_SUCCESS;
 }
 
 
-/*
- Mostly just assign the right data type for the bignum.
- */
-inline static QCBORError DecodeBigNum(QCBORItem *pDecodedItem)
-{
-   // Stack Use: UsefulBuf 1  -- 16
-   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
-      return QCBOR_ERR_BAD_OPT_TAG;
-   }
-   const UsefulBufC Temp    = pDecodedItem->val.string;
-   pDecodedItem->val.bigNum = Temp;
-   const bool bIsPosBigNum = (bool)(pDecodedItem->uTagBits & QCBOR_TAGFLAG_POS_BIGNUM);
-   pDecodedItem->uDataType  = (uint8_t)(bIsPosBigNum ? QCBOR_TYPE_POSBIGNUM
-                                                     : QCBOR_TYPE_NEGBIGNUM);
-   return QCBOR_SUCCESS;
-}
-
 
 /*
  The epoch formatted date. Turns lots of different forms of encoding
@@ -1186,8 +1641,7 @@
  */
 static QCBORError DecodeDateEpoch(QCBORItem *pDecodedItem)
 {
-   // Stack usage: 1
-   QCBORError nReturn = QCBOR_SUCCESS;
+   QCBORError uReturn = QCBOR_SUCCESS;
 
    pDecodedItem->val.epochDate.fSecondsFraction = 0;
 
@@ -1198,11 +1652,10 @@
          break;
 
       case QCBOR_TYPE_UINT64:
-         if(pDecodedItem->val.uint64 > INT64_MAX) {
-            nReturn = QCBOR_ERR_DATE_OVERFLOW;
-            goto Done;
-         }
-         pDecodedItem->val.epochDate.nSeconds = (int64_t)pDecodedItem->val.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:
@@ -1228,13 +1681,16 @@
          //
          // Without the 0x7ff there is a ~30 minute range of time
          // values 10 billion years in the past and in the future
-         // where this this code would go wrong and some compilers
-         // will generate warnings or errors.
+         // where this code would go wrong. Some compilers
+         // will generate warnings or errors without the 0x7ff
+         // because of the precision issue.
          const double d = pDecodedItem->uDataType == QCBOR_TYPE_DOUBLE ?
                             pDecodedItem->val.dfnum :
                             (double)pDecodedItem->val.fnum;
-         if(d > (double)(INT64_MAX - 0x7ff)) {
-            nReturn = QCBOR_ERR_DATE_OVERFLOW;
+         if(isnan(d) ||
+            d > (double)(INT64_MAX - 0x7ff) ||
+            d < (double)(INT64_MIN + 0x7ff)) {
+            uReturn = QCBOR_ERR_DATE_OVERFLOW;
             goto Done;
          }
          pDecodedItem->val.epochDate.nSeconds = (int64_t)d;
@@ -1242,24 +1698,40 @@
                            d - (double)pDecodedItem->val.epochDate.nSeconds;
       }
 #else
-         /* Disabling float support causes a floating point
-          data to error in the default below. The above code
-          requires floating point conversion to integers and
-          comparison which requires either floating point HW
-          or a SW library. */
-         nReturn = QCBOR_ERR_FLOAT_DATE_UNSUPPORTED;
+
+         uReturn = QCBOR_ERR_FLOAT_DATE_DISABLED;
+         goto Done;
+
 #endif /* QCBOR_DISABLE_FLOAT_HW_USE */
          break;
 
-
       default:
-         nReturn = QCBOR_ERR_BAD_OPT_TAG;
+         uReturn = QCBOR_ERR_BAD_OPT_TAG;
          goto Done;
    }
+
    pDecodedItem->uDataType = QCBOR_TYPE_DATE_EPOCH;
 
 Done:
-   return nReturn;
+   return uReturn;
+}
+
+
+/*
+ Mostly just assign the right data type for the bignum.
+ */
+static inline QCBORError DecodeBigNum(QCBORItem *pDecodedItem)
+{
+   // Stack Use: UsefulBuf 1  -- 16
+   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   const UsefulBufC Temp    = pDecodedItem->val.string;
+   pDecodedItem->val.bigNum = Temp;
+   const bool bIsPosBigNum = (bool)(pDecodedItem->uTags[0] == CBOR_TAG_POS_BIGNUM);
+   pDecodedItem->uDataType  = (uint8_t)(bIsPosBigNum ? QCBOR_TYPE_POSBIGNUM
+                                                     : QCBOR_TYPE_NEGBIGNUM);
+   return QCBOR_SUCCESS;
 }
 
 
@@ -1274,7 +1746,7 @@
  This will fetch and decode the exponent and mantissa and put the
  result back into pDecodedItem.
  */
-inline static QCBORError
+static inline QCBORError
 QCBORDecode_MantissaAndExponent(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
 {
    QCBORError nReturn;
@@ -1297,7 +1769,7 @@
 
    // --- Get the exponent ---
    QCBORItem exponentItem;
-   nReturn = QCBORDecode_GetNextMapOrArray(me, &exponentItem, NULL);
+   nReturn = QCBORDecode_GetNextMapOrArray(me, &exponentItem);
    if(nReturn != QCBOR_SUCCESS) {
       goto Done;
    }
@@ -1337,7 +1809,8 @@
       // 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;
-   }  else if(mantissaItem.uDataType == QCBOR_TYPE_POSBIGNUM || mantissaItem.uDataType == QCBOR_TYPE_NEGBIGNUM) {
+   }  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;
       // Depends on numbering of QCBOR_TYPE_XXX
@@ -1352,18 +1825,237 @@
 
    // --- Check that array only has the two numbers ---
    if(mantissaItem.uNextNestLevel == nNestLevel) {
-      // Extra items in the decimal fraction / big num
+      // Extra items in the decimal fraction / big float
       nReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
       goto Done;
    }
+   pDecodedItem->uNextNestLevel = mantissaItem.uNextNestLevel;
 
 Done:
-
   return nReturn;
 }
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 
 
+static inline QCBORError DecodeURI(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QCBOR_TYPE_URI;
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError DecodeB64URL(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QCBOR_TYPE_BASE64URL;
+
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError DecodeB64(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QCBOR_TYPE_BASE64;
+
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError DecodeRegex(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QCBOR_TYPE_REGEX;
+
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError DecodeWrappedCBOR(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QBCOR_TYPE_WRAPPED_CBOR;
+
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError DecodeWrappedCBORSequence(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE;
+
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError 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 {
+      return QCBOR_ERR_BAD_OPT_TAG;
+
+   }
+
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError DecodeUUID(QCBORItem *pDecodedItem)
+{
+   if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+      return QCBOR_ERR_BAD_OPT_TAG;
+   }
+   pDecodedItem->uDataType = QCBOR_TYPE_UUID;
+
+   return QCBOR_SUCCESS;
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+ */
+static QCBORError
+QCBORDecode_GetNextTag(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+   QCBORError uReturn;
+
+   uReturn = QCBORDecode_GetNextMapOrArray(me, pDecodedItem);
+   if(uReturn != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   for(unsigned uTagIndex = 0; uTagIndex < QCBOR_MAX_TAGS_PER_ITEM; uTagIndex++) {
+      switch(pDecodedItem->uTags[uTagIndex]) {
+
+         // Many of the functions here only just map a CBOR tag to
+         // a QCBOR_TYPE for a string and could probably be
+         // implemented with less object code. This implementation
+         // of string types takes about 120 bytes of object code
+         // (that is always linked and not removed by dead stripping).
+         case CBOR_TAG_DATE_STRING:
+         uReturn = DecodeDateString(pDecodedItem);
+         break;
+
+         case CBOR_TAG_DATE_EPOCH:
+         uReturn = DecodeDateEpoch(pDecodedItem);
+         break;
+
+         case CBOR_TAG_POS_BIGNUM:
+         case CBOR_TAG_NEG_BIGNUM:
+         uReturn = DecodeBigNum(pDecodedItem);
+         break;
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+         case CBOR_TAG_DECIMAL_FRACTION:
+         case CBOR_TAG_BIGFLOAT:
+         // For aggregate tagged types, what goes into pTags is only collected
+         // from the surrounding data item, not the contents, so pTags is not
+         // passed on here.
+
+         uReturn = QCBORDecode_MantissaAndExponent(me, pDecodedItem);
+         break;
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+         case CBOR_TAG_CBOR:
+         uReturn = DecodeWrappedCBOR(pDecodedItem);
+         break;
+
+         case CBOR_TAG_CBOR_SEQUENCE:
+         uReturn = DecodeWrappedCBORSequence(pDecodedItem);
+         break;
+
+         case CBOR_TAG_URI:
+         uReturn = DecodeURI(pDecodedItem);
+         break;
+
+         case CBOR_TAG_B64URL:
+         uReturn = DecodeB64URL(pDecodedItem);
+         break;
+
+         case CBOR_TAG_B64:
+         uReturn = DecodeB64(pDecodedItem);
+         break;
+
+         case CBOR_TAG_MIME:
+         case CBOR_TAG_BINARY_MIME:
+         uReturn = DecodeMIME(pDecodedItem);
+         break;
+
+         case CBOR_TAG_REGEX:
+         uReturn = DecodeRegex(pDecodedItem);
+         break;
+
+         case CBOR_TAG_BIN_UUID:
+         uReturn = DecodeUUID(pDecodedItem);
+         break;
+
+         case CBOR_TAG_INVALID16:
+         // The end of the tag list or no tags
+         // Successful exit from the loop.
+         goto Done;
+
+         default:
+         // A tag that is not understood
+         // A successful exit from the loop
+         goto Done;
+
+      }
+      if(uReturn != QCBOR_SUCCESS) {
+         goto Done;
+      }
+      // A tag was successfully processed, shift it
+      // out of the list of tags returned.
+      ShiftTags(pDecodedItem);
+   }
+
+Done:
+   return uReturn;
+}
+
+
+QCBORError
+QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
+{
+   QCBORError uErr;
+   uErr = QCBORDecode_GetNextTag(pMe, pDecodedItem);
+   if(uErr != QCBOR_SUCCESS) {
+      pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
+      pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
+   }
+   return uErr;
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_VGetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, pDecodedItem);
+}
+
+
 /*
  Public function, see header qcbor/qcbor_decode.h file
  */
@@ -1374,70 +2066,28 @@
 {
    QCBORError nReturn;
 
-   nReturn = QCBORDecode_GetNextMapOrArray(me, pDecodedItem, pTags);
+   nReturn = QCBORDecode_GetNext(me, pDecodedItem);
    if(nReturn != QCBOR_SUCCESS) {
-      goto Done;
+      return nReturn;
    }
 
-#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
-#define TAG_MAPPER_FIRST_XXX TAG_MAPPER_FIRST_SIX
-#else
-#define TAG_MAPPER_FIRST_XXX TAG_MAPPER_FIRST_FOUR
-#endif
-
-   // Only pay attention to tags this code knows how to decode.
-   switch(pDecodedItem->uTagBits & TAG_MAPPER_FIRST_XXX) {
-      case 0:
-         // No tags at all or none we know about. Nothing to do.
-         // This is the pass-through path of this function
-         // that will mostly be taken when decoding any item.
-         break;
-
-      case QCBOR_TAGFLAG_DATE_STRING:
-         nReturn = DecodeDateString(pDecodedItem);
-         break;
-
-      case QCBOR_TAGFLAG_DATE_EPOCH:
-         nReturn = DecodeDateEpoch(pDecodedItem);
-         break;
-
-      case QCBOR_TAGFLAG_POS_BIGNUM:
-      case QCBOR_TAGFLAG_NEG_BIGNUM:
-         nReturn = DecodeBigNum(pDecodedItem);
-         break;
-
-#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
-      case QCBOR_TAGFLAG_DECIMAL_FRACTION:
-      case QCBOR_TAGFLAG_BIGFLOAT:
-         // For aggregate tagged types, what goes into pTags is only collected
-         // from the surrounding data item, not the contents, so pTags is not
-         // passed on here.
-
-         nReturn = QCBORDecode_MantissaAndExponent(me, pDecodedItem);
-         break;
-#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
-
-      default:
-         // Encountering some mixed-up CBOR like something that
-         // is tagged as both a string and integer date.
-         nReturn = QCBOR_ERR_BAD_OPT_TAG;
+   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] = ConvertTag(me,pDecodedItem->uTags[nTagIndex]);
+         pTags->uNumUsed++;
+      }
    }
 
-Done:
-   if(nReturn != QCBOR_SUCCESS) {
-      pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
-      pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
-   }
-   return nReturn;
-}
-
-
-/*
- Public function, see header qcbor/qcbor_decode.h file
- */
-QCBORError QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
-{
-   return QCBORDecode_GetNextWithTags(me, pDecodedItem, NULL);
+   return QCBOR_SUCCESS;
 }
 
 
@@ -1455,7 +2105,7 @@
  - QCBORDecode_GetNextMapOrArray - This manages the beginnings and
  ends of maps and arrays. It tracks descending into and ascending
  out of maps/arrays. It processes all breaks that terminate
- maps and arrays.
+ indefinite length maps and arrays.
 
  - GetNext_MapEntry -- This handles the combining of two
  items, the label and the data, that make up a map entry.
@@ -1487,21 +2137,20 @@
 /*
  Public function, see header qcbor/qcbor_decode.h file
  */
-int QCBORDecode_IsTagged(QCBORDecodeContext *me,
-                         const QCBORItem *pItem,
-                         uint64_t uTag)
+bool 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;
+   for(unsigned uTagIndex = 0; uTagIndex < QCBOR_MAX_TAGS_PER_ITEM; uTagIndex++) {
+      if(pItem->uTags[uTagIndex] == CBOR_TAG_INVALID16) {
+         break;
+      }
+      if(ConvertTag(me, pItem->uTags[uTagIndex]) == uTag) {
+         return true;
+      }
    }
 
-   const uint64_t uTagBit = 0x01ULL << uTagBitIndex;
-   return (uTagBit & pItem->uTagBits) != 0;
+   return false;
 }
 
 
@@ -1510,17 +2159,21 @@
  */
 QCBORError QCBORDecode_Finish(QCBORDecodeContext *me)
 {
-   QCBORError nReturn = QCBOR_SUCCESS;
+   QCBORError uReturn = me->uLastError;
+
+   if(uReturn != QCBOR_SUCCESS) {
+      goto Done;
+   }
 
    // Error out if all the maps/arrays are not closed out
-   if(DecodeNesting_IsNested(&(me->nesting))) {
-      nReturn = QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN;
+   if(!DecodeNesting_IsCurrentAtTop(&(me->nesting))) {
+      uReturn = QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED;
       goto Done;
    }
 
    // Error out if not all the bytes are consumed
    if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
-      nReturn = QCBOR_ERR_EXTRA_BYTES;
+      uReturn = QCBOR_ERR_EXTRA_BYTES;
    }
 
 Done:
@@ -1528,10 +2181,37 @@
    // Always called, even if there are errors; always have to clean up
    StringAllocator_Destruct(&(me->StringAllocator));
 
-   return nReturn;
+   return uReturn;
 }
 
 
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+// Improvement: make these inline?
+uint64_t QCBORDecode_GetNthTag(QCBORDecodeContext *pMe,
+                               const QCBORItem    *pItem,
+                               uint32_t            uIndex)
+{
+   if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
+      return CBOR_TAG_INVALID64;
+   } else {
+      return ConvertTag(pMe, pItem->uTags[uIndex]);
+   }
+}
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+uint64_t QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pMe,
+                                     uint32_t                  uIndex)
+{
+   if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) {
+      return CBOR_TAG_INVALID64;
+   } else {
+      return ConvertTag(pMe, pMe->uLastTags[uIndex]);
+   }
+}
 
 /*
 
@@ -1549,10 +2229,10 @@
    string and an epoch date QCBOR_ERR_UNSUPPORTED
 
  - Encontered an array or mapp that has too many items
-   QCBOR_ERR_ARRAY_TOO_LONG
+   QCBOR_ERR_ARRAY_DECODE_TOO_LONG
 
  - Encountered array/map nesting that is too deep
-   QCBOR_ERR_ARRAY_NESTING_TOO_DEEP
+   QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP
 
  - An epoch date > INT64_MAX or < INT64_MIN was encountered
    QCBOR_ERR_DATE_OVERFLOW
@@ -1602,7 +2282,7 @@
 
    // Just assume the size here. It was checked during SetUp so
    // the assumption is safe.
-   UsefulInputBuf_Init(&UIB, (UsefulBufC){pMem, QCBOR_DECODE_MIN_MEM_POOL_SIZE});
+   UsefulInputBuf_Init(&UIB, (UsefulBufC){pMem,QCBOR_DECODE_MIN_MEM_POOL_SIZE});
    *puPoolSize     = UsefulInputBuf_GetUint32(&UIB);
    *puFreeOffset   = UsefulInputBuf_GetUint32(&UIB);
    return UsefulInputBuf_GetError(&UIB);
@@ -1725,19 +2405,19 @@
    // constant in the header is correct.  This check should optimize
    // down to nothing.
    if(QCBOR_DECODE_MIN_MEM_POOL_SIZE < 2 * sizeof(uint32_t)) {
-      return QCBOR_ERR_BUFFER_TOO_SMALL;
+      return QCBOR_ERR_MEM_POOL_SIZE;
    }
 
    // The pool size and free offset packed in to the beginning of pool
    // memory are only 32-bits. This check will optimize out on 32-bit
    // machines.
    if(Pool.len > UINT32_MAX) {
-      return QCBOR_ERR_BUFFER_TOO_LARGE;
+      return QCBOR_ERR_MEM_POOL_SIZE;
    }
 
    // This checks that the pool buffer given is big enough.
    if(MemPool_Pack(Pool, QCBOR_DECODE_MIN_MEM_POOL_SIZE)) {
-      return QCBOR_ERR_BUFFER_TOO_SMALL;
+      return QCBOR_ERR_MEM_POOL_SIZE;
    }
 
    pMe->StringAllocator.pfAllocator    = MemPool_Function;
@@ -1746,3 +2426,3030 @@
 
    return QCBOR_SUCCESS;
 }
+
+
+
+static inline void CopyTags(QCBORDecodeContext *pMe, const QCBORItem *pItem)
+{
+   memcpy(pMe->uLastTags, pItem->uTags, sizeof(pItem->uTags));
+}
+
+
+/*
+ Consume an entire map or array (and do next to
+ nothing for non-aggregate types).
+ */
+static inline QCBORError
+ConsumeItem(QCBORDecodeContext *pMe,
+            const QCBORItem    *pItemToConsume,
+            uint_fast8_t       *puNextNestLevel)
+{
+   QCBORError uReturn;
+   QCBORItem  Item;
+
+   // If it is a map or array, this will tell if it is empty.
+   const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel);
+
+   if(QCBORItem_IsMapOrArray(pItemToConsume) && !bIsEmpty) {
+      /* There is only real work to do for non-empty maps and arrays */
+
+      /* This works for definite and indefinite length
+       * maps and arrays by using the nesting level
+       */
+      do {
+         uReturn = QCBORDecode_GetNext(pMe, &Item);
+         if(QCBORDecode_IsUnrecoverableError(uReturn)) {
+            goto Done;
+         }
+      } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
+
+      *puNextNestLevel = Item.uNextNestLevel;
+
+      uReturn = QCBOR_SUCCESS;
+
+   } else {
+      /* item_to_consume is not a map or array */
+      /* Just pass the nesting level through */
+      *puNextNestLevel = pItemToConsume->uNextNestLevel;
+
+      uReturn = QCBOR_SUCCESS;
+   }
+
+Done:
+    return uReturn;
+}
+
+
+/* Return true if the labels in Item1 and Item2 are the same.
+   Works only for integer and string labels. Returns false
+   for any other type. */
+static inline bool
+MatchLabel(QCBORItem Item1, QCBORItem Item2)
+{
+   if(Item1.uLabelType == QCBOR_TYPE_INT64) {
+      if(Item2.uLabelType == QCBOR_TYPE_INT64 && Item1.label.int64 == Item2.label.int64) {
+         return true;
+      }
+   } else if(Item1.uLabelType == QCBOR_TYPE_TEXT_STRING) {
+      if(Item2.uLabelType == QCBOR_TYPE_TEXT_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) {
+         return true;
+      }
+   } else if(Item1.uLabelType == QCBOR_TYPE_BYTE_STRING) {
+      if(Item2.uLabelType == QCBOR_TYPE_BYTE_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) {
+         return true;
+      }
+   } else if(Item1.uLabelType == QCBOR_TYPE_UINT64) {
+      if(Item2.uLabelType == QCBOR_TYPE_UINT64 && Item1.label.uint64 == Item2.label.uint64) {
+         return true;
+      }
+   }
+
+   /* Other label types are never matched */
+   return false;
+}
+
+
+/*
+ Returns true if Item1 and Item2 are the same type
+ or if either are of QCBOR_TYPE_ANY.
+ */
+static inline bool
+MatchType(QCBORItem Item1, QCBORItem Item2)
+{
+   if(Item1.uDataType == Item2.uDataType) {
+      return true;
+   } else if(Item1.uDataType == QCBOR_TYPE_ANY) {
+      return true;
+   } else if(Item2.uDataType == QCBOR_TYPE_ANY) {
+      return true;
+   }
+   return false;
+}
+
+
+/**
+ @brief Search a map for a set of items.
+
+ @param[in]  pMe           The decode context to search.
+ @param[in,out] pItemArray The items to search for and the items found.
+ @param[out] puOffset      Byte offset of last item matched.
+ @param[in] pCBContext     Context for the not-found item call back.
+ @param[in] pfCallback     Function to call on items not matched in pItemArray.
+
+ @retval QCBOR_ERR_NOT_ENTERED Trying to search without having entered a map
+
+ @retval QCBOR_ERR_DUPLICATE_LABEL Duplicate items (items with the same label)
+                                   were found for one of the labels being
+                                   search for. This duplicate detection is
+                                   only performed for items in pItemArray,
+                                   not every item in the map.
+
+ @retval QCBOR_ERR_UNEXPECTED_TYPE A label was matched, but the type was
+                                   wrong for the matchd label.
+
+ @retval Also errors returned by QCBORDecode_GetNext().
+
+ On input pItemArray contains a list of labels and data types
+ of items to be found.
+
+ On output the fully retrieved items are filled in with
+ values and such. The label was matched, so it never changes.
+
+ If an item was not found, its data type is set to QCBOR_TYPE_NONE.
+
+ This also finds the ends of maps and arrays when they are exited.
+ */
+static QCBORError
+MapSearch(QCBORDecodeContext *pMe,
+          QCBORItem          *pItemArray,
+          size_t             *puOffset,
+          void               *pCBContext,
+          QCBORItemCallback   pfCallback)
+{
+   QCBORError uReturn;
+   uint64_t   uFoundItemBitMap = 0;
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      uReturn = pMe->uLastError;
+      goto Done2;
+   }
+
+   if(!DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_MAP) &&
+      pItemArray->uLabelType != QCBOR_TYPE_NONE) {
+      /* QCBOR_TYPE_NONE as first item indicates just looking
+         for the end of an array, so don't give error. */
+      uReturn = QCBOR_ERR_MAP_NOT_ENTERED;
+      goto Done2;
+   }
+
+   if(DecodeNesting_IsBoundedEmpty(&(pMe->nesting))) {
+      // It is an empty bounded array or map
+      if(pItemArray->uLabelType == QCBOR_TYPE_NONE) {
+         // Just trying to find the end of the map or array
+         pMe->uMapEndOffsetCache = DecodeNesting_GetMapOrArrayStart(&(pMe->nesting));
+         uReturn = QCBOR_SUCCESS;
+      } else {
+         // Nothing is ever found in an empty array or map. All items
+         // are marked as not found below.
+         uReturn = QCBOR_SUCCESS;
+      }
+      goto Done2;
+   }
+
+   QCBORDecodeNesting SaveNesting;
+   DecodeNesting_PrepareForMapSearch(&(pMe->nesting), &SaveNesting);
+
+   /* Reposition to search from the start of the map / array */
+   UsefulInputBuf_Seek(&(pMe->InBuf),
+                       DecodeNesting_GetMapOrArrayStart(&(pMe->nesting)));
+
+   /*
+    Loop over all the items in the map or array. Each item
+    could be a map or array, but label matching is only at
+    the main level. This handles definite and indefinite
+    length maps and arrays. The only reason this is ever
+    called on arrays is to find their end position.
+
+    This will always run over all items in order to do
+    duplicate detection.
+
+    This will exit with failure if it encounters an
+    unrecoverable error, but continue on for recoverable
+    errors.
+
+    If a recoverable error occurs on a matched item, then
+    that error code is returned.
+    */
+   const uint8_t uMapNestLevel = DecodeNesting_GetBoundedModeLevel(&(pMe->nesting));
+   uint_fast8_t  uNextNestLevel;
+   do {
+      /* Remember offset of the item because sometimes it has to be returned */
+      const size_t uOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+      /* Get the item */
+      QCBORItem Item;
+      QCBORError uResult = QCBORDecode_GetNextTag(pMe, &Item);
+      if(QCBORDecode_IsUnrecoverableError(uResult)) {
+         /* Unrecoverable error so map can't even be decoded. */
+         uReturn = uResult;
+         goto Done;
+      }
+      if(uResult == QCBOR_ERR_NO_MORE_ITEMS) {
+         // Unexpected end of map or array.
+         uReturn = uResult;
+         goto Done;
+      }
+
+      /* See if item has one of the labels that are of interest */
+      bool bMatched = false;
+      for(int nIndex = 0; pItemArray[nIndex].uLabelType != QCBOR_TYPE_NONE; nIndex++) {
+         if(MatchLabel(Item, pItemArray[nIndex])) {
+            /* A label match has been found */
+            if(uFoundItemBitMap & (0x01ULL << nIndex)) {
+               uReturn = QCBOR_ERR_DUPLICATE_LABEL;
+               goto Done;
+            }
+            /* Also try to match its type */
+            if(!MatchType(Item, pItemArray[nIndex])) {
+               uReturn = QCBOR_ERR_UNEXPECTED_TYPE;
+               goto Done;
+            }
+
+            if(uResult != QCBOR_SUCCESS) {
+               uReturn = uResult;
+               goto Done;
+            }
+
+            /* Successful match. Return the item. */
+            pItemArray[nIndex] = Item;
+            uFoundItemBitMap |= 0x01ULL << nIndex;
+            if(puOffset) {
+               *puOffset = uOffset;
+            }
+            bMatched = true;
+         }
+      }
+
+
+      if(!bMatched && pfCallback != NULL) {
+         /*
+          Call the callback on unmatched labels.
+          (It is tempting to do duplicate detection here, but that would
+          require dynamic memory allocation because the number of labels
+          that might be encountered is unbounded.)
+         */
+         uReturn = (*pfCallback)(pCBContext, &Item);
+         if(uReturn != QCBOR_SUCCESS) {
+            goto Done;
+         }
+      }
+
+      /*
+       Consume the item whether matched or not. This
+       does the work of traversing maps and array and
+       everything in them. In this loop only the
+       items at the current nesting level are examined
+       to match the labels.
+       */
+      uReturn = ConsumeItem(pMe, &Item, &uNextNestLevel);
+      if(uReturn != QCBOR_SUCCESS) {
+         goto Done;
+      }
+
+   } while (uNextNestLevel >= uMapNestLevel);
+
+   uReturn = QCBOR_SUCCESS;
+
+   const size_t uEndOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+   // Check here makes sure that this won't accidentally be
+   // QCBOR_MAP_OFFSET_CACHE_INVALID which is larger than
+   // QCBOR_MAX_DECODE_INPUT_SIZE.
+   if(uEndOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) {
+      uReturn = QCBOR_ERR_INPUT_TOO_LARGE;
+      goto Done;
+   }
+   /* Cast OK because encoded CBOR is limited to UINT32_MAX */
+   pMe->uMapEndOffsetCache = (uint32_t)uEndOffset;
+
+ Done:
+   DecodeNesting_RestoreFromMapSearch(&(pMe->nesting), &SaveNesting);
+
+ Done2:
+   /* For all items not found, set the data and label type to QCBOR_TYPE_NONE */
+   for(int i = 0; pItemArray[i].uLabelType != 0; i++) {
+      if(!(uFoundItemBitMap & (0x01ULL << i))) {
+         pItemArray[i].uDataType  = QCBOR_TYPE_NONE;
+         pItemArray[i].uLabelType = QCBOR_TYPE_NONE;
+      }
+   }
+
+   return uReturn;
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetItemInMapN(QCBORDecodeContext *pMe,
+                               int64_t             nLabel,
+                               uint8_t             uQcborType,
+                               QCBORItem          *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   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 = MapSearch(pMe, OneItemSeach, NULL, NULL, NULL);
+
+   *pItem = OneItemSeach[0];
+
+   if(uReturn != QCBOR_SUCCESS) {
+      goto Done;
+   }
+   if(OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
+      uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
+   }
+
+ Done:
+   pMe->uLastError = (uint8_t)uReturn;
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe,
+                                const char         *szLabel,
+                                uint8_t             uQcborType,
+                                QCBORItem          *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   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 = MapSearch(pMe, OneItemSeach, NULL, NULL, NULL);
+   if(uReturn != QCBOR_SUCCESS) {
+      goto Done;
+   }
+   if(OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
+      uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
+      goto Done;
+   }
+
+   *pItem = OneItemSeach[0];
+
+Done:
+   pMe->uLastError = (uint8_t)uReturn;
+}
+
+
+
+static QCBORError
+CheckTypeList(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]) {
+         return QCBOR_SUCCESS;
+      }
+   }
+   return QCBOR_ERR_UNEXPECTED_TYPE;
+}
+
+
+/**
+ @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
+
+ The data type must be one of the QCBOR_TYPEs, not the IETF CBOR Registered
+ tag value.
+ */
+static QCBORError
+CheckTagRequirement(const TagSpecification TagSpec, const QCBORItem *pItem)
+{
+   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;
+   }
+
+   const int nTagReq = TagSpec.uTagRequirement & ~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS;
+   const int nItemType = pItem->uDataType;
+
+   if(nTagReq == QCBOR_TAG_REQUIREMENT_TAG) {
+      // Must match the tag and only the tag
+      return CheckTypeList(nItemType, TagSpec.uTaggedTypes);
+   }
+
+   QCBORError uReturn = 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;
+   }
+
+   /* 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
+    also the case that the CBOR standard discourages. */
+
+   return CheckTypeList(nItemType, TagSpec.uTaggedTypes);
+}
+
+
+
+// This could be semi-private if need be
+static inline
+void QCBORDecode_GetTaggedItemInMapN(QCBORDecodeContext *pMe,
+                                     int64_t             nLabel,
+                                     TagSpecification    TagSpec,
+                                     QCBORItem          *pItem)
+{
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, pItem);
+}
+
+
+// This could be semi-private if need be
+static inline
+void QCBORDecode_GetTaggedItemInMapSZ(QCBORDecodeContext *pMe,
+                                     const char          *szLabel,
+                                     TagSpecification    TagSpec,
+                                     QCBORItem          *pItem)
+{
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, pItem);
+}
+
+// Semi-private
+void QCBORDecode_GetTaggedStringInMapN(QCBORDecodeContext *pMe,
+                                       int64_t             nLabel,
+                                       TagSpecification    TagSpec,
+                                       UsefulBufC          *pString)
+{
+   QCBORItem Item;
+   QCBORDecode_GetTaggedItemInMapN(pMe, nLabel, TagSpec, &Item);
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      *pString = Item.val.string;
+   }
+}
+
+// Semi-private
+void QCBORDecode_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe,
+                                        const char *        szLabel,
+                                        TagSpecification    TagSpec,
+                                        UsefulBufC          *pString)
+{
+   QCBORItem Item;
+   QCBORDecode_GetTaggedItemInMapSZ(pMe, szLabel, TagSpec, &Item);
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      *pString = Item.val.string;
+   }
+}
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemList)
+{
+   QCBORError uErr = MapSearch(pMe, pItemList, NULL, NULL, NULL);
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetItemsInMapWithCallback(QCBORDecodeContext *pMe,
+                                           QCBORItem          *pItemList,
+                                           void               *pCallbackCtx,
+                                           QCBORItemCallback   pfCB)
+{
+   QCBORError uErr = MapSearch(pMe, pItemList, NULL, pCallbackCtx, pfCB);
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+static void SearchAndEnter(QCBORDecodeContext *pMe, QCBORItem pSearch[])
+{
+   // 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.
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state; do nothing.
+      return;
+   }
+
+   size_t uOffset;
+   pMe->uLastError = (uint8_t)MapSearch(pMe, pSearch, &uOffset, NULL, NULL);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   if(pSearch->uDataType == QCBOR_TYPE_NONE) {
+      pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND;
+      return;
+   }
+
+   /* Need to get the current pre-order nesting level and cursor to be
+      at the map/array about to be entered.
+
+    Also need the current map nesting level and start cursor to
+    be at the right place.
+
+    The UsefulInBuf offset could be anywhere, so no assumption is
+    made about it.
+
+    No assumption is made about the pre-order nesting level either.
+
+    However the bounded mode nesting level is assumed to be one above
+    the map level that is being entered.
+    */
+   /* Seek to the data item that is the map or array */
+   UsefulInputBuf_Seek(&(pMe->InBuf), uOffset);
+
+   DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting));
+
+   QCBORDecode_EnterBoundedMapOrArray(pMe, pSearch->uDataType, NULL);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_EnterMapFromMapN(QCBORDecodeContext *pMe, int64_t nLabel)
+{
+   QCBORItem OneItemSeach[2];
+   OneItemSeach[0].uLabelType  = QCBOR_TYPE_INT64;
+   OneItemSeach[0].label.int64 = nLabel;
+   OneItemSeach[0].uDataType   = QCBOR_TYPE_MAP;
+   OneItemSeach[1].uLabelType  = QCBOR_TYPE_NONE;
+
+   /* The map to enter was found, now finish off entering it. */
+   SearchAndEnter(pMe, OneItemSeach);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel)
+{
+   QCBORItem OneItemSeach[2];
+   OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+   OneItemSeach[0].uDataType    = QCBOR_TYPE_MAP;
+   OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE;
+
+   SearchAndEnter(pMe, OneItemSeach);
+}
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_EnterArrayFromMapN(QCBORDecodeContext *pMe, int64_t nLabel)
+{
+   QCBORItem OneItemSeach[2];
+   OneItemSeach[0].uLabelType  = QCBOR_TYPE_INT64;
+   OneItemSeach[0].label.int64 = nLabel;
+   OneItemSeach[0].uDataType   = QCBOR_TYPE_ARRAY;
+   OneItemSeach[1].uLabelType  = QCBOR_TYPE_NONE;
+
+   SearchAndEnter(pMe, OneItemSeach);
+}
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel)
+{
+   QCBORItem OneItemSeach[2];
+   OneItemSeach[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+   OneItemSeach[0].uDataType    = QCBOR_TYPE_ARRAY;
+   OneItemSeach[1].uLabelType   = QCBOR_TYPE_NONE;
+
+   SearchAndEnter(pMe, OneItemSeach);
+}
+
+
+// Semi-private function
+void QCBORDecode_EnterBoundedMapOrArray(QCBORDecodeContext *pMe, uint8_t uType, QCBORItem *pItem)
+{
+    QCBORError uErr;
+
+   /* Must only be called on maps and arrays. */
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state; do nothing.
+      return;
+   }
+
+   /* Get the data item that is the map or array being entered. */
+   QCBORItem Item;
+   uErr = QCBORDecode_GetNext(pMe, &Item);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+   if(Item.uDataType != uType) {
+      uErr = QCBOR_ERR_UNEXPECTED_TYPE;
+      goto Done;
+   }
+
+   CopyTags(pMe, &Item);
+
+
+   const bool bIsEmpty = (Item.uNextNestLevel <= Item.uNestingLevel);
+   if(bIsEmpty) {
+      if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) {
+         // Undo decrement done by QCBORDecode_GetNext() so the the
+         // the decrement when exiting the map/array works correctly
+         pMe->nesting.pCurrent->u.ma.uCountCursor++;
+      }
+      // Special case to increment nesting level for zero-length maps
+      // and arrays entered in bounded mode.
+      DecodeNesting_Descend(&(pMe->nesting), uType);
+   }
+
+   pMe->uMapEndOffsetCache = QCBOR_MAP_OFFSET_CACHE_INVALID;
+
+   uErr = DecodeNesting_EnterBoundedMapOrArray(&(pMe->nesting), bIsEmpty,
+                                               UsefulInputBuf_Tell(&(pMe->InBuf)));
+
+   if(pItem != NULL) {
+      *pItem = Item;
+   }
+
+Done:
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+/*
+ This is the common work for exiting a level that is a bounded map,
+ array or bstr wrapped CBOR.
+
+ One chunk of work is to set up the pre-order traversal so it is at
+ the item just after the bounded map, array or bstr that is being
+ exited. This is somewhat complex.
+
+ The other work is to level-up the bounded mode to next higest bounded
+ mode or the top level if there isn't one.
+ */
+static QCBORError
+ExitBoundedLevel(QCBORDecodeContext *pMe, uint32_t uEndOffset)
+{
+   QCBORError uErr;
+
+   /*
+    First the pre-order-traversal byte offset is positioned to the
+    item just after the bounded mode item that was just consumed.
+    */
+   UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset);
+
+   /*
+    Next, set the current nesting level to one above the bounded level
+    that was just exited.
+
+    DecodeNesting_CheckBoundedType() is always called before this and
+    makes sure pCurrentBounded is valid.
+    */
+   DecodeNesting_LevelUpCurrent(&(pMe->nesting));
+
+   /*
+    This does the complex work of leveling up the pre-order traversal
+    when the end of a map or array or another bounded level is
+    reached.  It may do nothing, or ascend all the way to the top
+    level.
+    */
+   uErr = NestLevelAscender(pMe, false);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   /*
+    This makes the next highest bounded level the current bounded
+    level. If there is no next highest level, then no bounded mode is
+    in effect.
+    */
+   DecodeNesting_LevelUpBounded(&(pMe->nesting));
+
+   pMe->uMapEndOffsetCache = QCBOR_MAP_OFFSET_CACHE_INVALID;
+
+Done:
+   return uErr;
+}
+
+
+// Semi-private function
+void QCBORDecode_ExitBoundedMapOrArray(QCBORDecodeContext *pMe, uint8_t uType)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      /* Already in error state; do nothing. */
+      return;
+   }
+
+   QCBORError uErr;
+
+   if(!DecodeNesting_IsBoundedType(&(pMe->nesting), uType)) {
+      uErr = QCBOR_ERR_EXIT_MISMATCH;
+      goto Done;
+   }
+
+   /*
+    Have to set the offset to the end of the map/array
+    that is being exited. If there is no cached value,
+    from previous map search, then do a dummy search.
+    */
+   if(pMe->uMapEndOffsetCache == QCBOR_MAP_OFFSET_CACHE_INVALID) {
+      QCBORItem Dummy;
+      Dummy.uLabelType = QCBOR_TYPE_NONE;
+      uErr = MapSearch(pMe, &Dummy, NULL, NULL, NULL);
+      if(uErr != QCBOR_SUCCESS) {
+         goto Done;
+      }
+   }
+
+   uErr = ExitBoundedLevel(pMe, pMe->uMapEndOffsetCache);
+
+Done:
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+
+static QCBORError InternalEnterBstrWrapped(QCBORDecodeContext *pMe,
+                                           const QCBORItem    *pItem,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC         *pBstr)
+{
+   if(pBstr) {
+      *pBstr = NULLUsefulBufC;
+   }
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state; do nothing.
+      return pMe->uLastError;
+   }
+
+   QCBORError uError = QCBOR_SUCCESS;
+
+   if(pItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
+      uError = QCBOR_ERR_UNEXPECTED_TYPE;
+      goto Done;;
+   }
+
+   const TagSpecification 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 = CheckTagRequirement(TagSpec, pItem);
+   if(uError != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) {
+      // Reverse the decrement done by GetNext() for the bstr so the
+      // increment in NestLevelAscender() called by ExitBoundedLevel()
+      // will work right.
+      DecodeNesting_ReverseDecrement(&(pMe->nesting));
+   }
+
+   if(pBstr) {
+      *pBstr = pItem->val.string;
+   }
+
+   // This saves the current length of the UsefulInputBuf and then
+   // narrows the UsefulInputBuf to start and length of the wrapped
+   // CBOR that is being entered.
+   //
+   // This makes sure the length is less than
+   // QCBOR_MAX_DECODE_INPUT_SIZE which is slighly less than
+   // UINT32_MAX. The value UINT32_MAX is used as a special indicator
+   // value. The checks against QCBOR_MAX_DECODE_INPUT_SIZE also make
+   // the casts safe.  uEndOfBstr will always be less than
+   // uPreviousLength because of the way UsefulInputBuf works so there
+   // is no need to check it.  There is also a range check in the
+   // seek.
+   //
+   // Most of these calls are simple inline accessors so this doesn't
+   // amount to much code.
+   const size_t uPreviousLength = UsefulInputBuf_GetBufferLength(&(pMe->InBuf));
+   if(uPreviousLength >= QCBOR_MAX_DECODE_INPUT_SIZE) {
+      uError = QCBOR_ERR_INPUT_TOO_LARGE;
+      goto Done;
+   }
+   const size_t uEndOfBstr = UsefulInputBuf_Tell(&(pMe->InBuf));
+   UsefulInputBuf_Seek(&(pMe->InBuf), uEndOfBstr - pItem->val.string.len);
+   UsefulInputBuf_SetBufferLength(&(pMe->InBuf), uEndOfBstr);
+
+   uError = DecodeNesting_DescendIntoBstrWrapped(&(pMe->nesting),
+                                                 (uint32_t)uPreviousLength,
+                                                 (uint32_t)uEndOfBstr);
+Done:
+   return uError;
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pMe,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC         *pBstr)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state; do nothing.
+      return;
+   }
+
+   /* Get the data item that is the map that is being searched */
+   QCBORItem Item;
+   pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)InternalEnterBstrWrapped(pMe,
+                                                       &Item,
+                                                       uTagRequirement,
+                                                       pBstr);
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pMe,
+                                          int64_t             nLabel,
+                                          uint8_t             uTagRequirement,
+                                          UsefulBufC         *pBstr)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+
+   pMe->uLastError = (uint8_t)InternalEnterBstrWrapped(pMe,
+                                                       &Item,
+                                                       uTagRequirement,
+                                                       pBstr);
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pMe,
+                                           const char         *szLabel,
+                                           uint8_t             uTagRequirement,
+                                           UsefulBufC         *pBstr)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+
+   pMe->uLastError = (uint8_t)InternalEnterBstrWrapped(pMe,
+                                                       &Item,
+                                                       uTagRequirement,
+                                                       pBstr);
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pMe)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state; do nothing.
+      return;
+   }
+
+   if(!DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_BYTE_STRING)) {
+      pMe->uLastError = QCBOR_ERR_EXIT_MISMATCH;
+      return;
+   }
+
+   /*
+    Reset the length of the UsefulInputBuf to what it was before
+    the bstr wrapped CBOR was entered.
+    */
+   UsefulInputBuf_SetBufferLength(&(pMe->InBuf),
+                               DecodeNesting_GetPreviousBoundedEnd(&(pMe->nesting)));
+
+
+   QCBORError uErr = ExitBoundedLevel(pMe, DecodeNesting_GetEndOfBstr(&(pMe->nesting)));
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+
+
+
+
+
+
+static QCBORError
+InterpretBool(QCBORDecodeContext *pMe, const QCBORItem *pItem, bool *pBool)
+{
+   switch(pItem->uDataType) {
+      case QCBOR_TYPE_TRUE:
+         *pBool = true;
+         return QCBOR_SUCCESS;
+         break;
+
+      case QCBOR_TYPE_FALSE:
+         *pBool = false;
+         return QCBOR_SUCCESS;
+         break;
+
+      default:
+         return QCBOR_ERR_UNEXPECTED_TYPE;
+         break;
+   }
+   CopyTags(pMe, pItem);
+}
+
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBool(QCBORDecodeContext *pMe, bool *pValue)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state, do nothing
+      return;
+   }
+
+   QCBORError nError;
+   QCBORItem  Item;
+
+   nError = QCBORDecode_GetNext(pMe, &Item);
+   if(nError != QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)nError;
+      return;
+   }
+   pMe->uLastError = (uint8_t)InterpretBool(pMe, &Item, pValue);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBoolInMapN(QCBORDecodeContext *pMe, int64_t nLabel, bool *pValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+
+   pMe->uLastError = (uint8_t)InterpretBool(pMe, &Item, pValue);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBoolInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, bool *pValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+
+   pMe->uLastError = (uint8_t)InterpretBool(pMe, &Item, pValue);
+}
+
+
+
+
+static void ProcessEpochDate(QCBORDecodeContext *pMe,
+                             QCBORItem           *pItem,
+                             uint8_t              uTagRequirement,
+                             int64_t             *pnTime)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state, do nothing
+      return;
+   }
+
+   QCBORError uErr;
+
+   const TagSpecification 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 = CheckTagRequirement(TagSpec, pItem);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   if(pItem->uDataType != QCBOR_TYPE_DATE_EPOCH) {
+      uErr = DecodeDateEpoch(pItem);
+      if(uErr != QCBOR_SUCCESS) {
+         goto Done;
+      }
+   }
+
+   // Save the tags in the last item's tags in the decode context
+   // for QCBORDecode_GetNthTagOfLast()
+   CopyTags(pMe, pItem);
+
+   *pnTime = pItem->val.epochDate.nSeconds;
+
+Done:
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+void QCBORDecode_GetEpochDate(QCBORDecodeContext *pMe,
+                              uint8_t             uTagRequirement,
+                              int64_t            *pnTime)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state, do nothing
+      return;
+   }
+
+   QCBORItem  Item;
+   pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, &Item);
+
+   ProcessEpochDate(pMe, &Item, uTagRequirement, pnTime);
+}
+
+
+void
+QCBORDecode_GetEpochDateInMapN(QCBORDecodeContext *pMe,
+                               int64_t             nLabel,
+                               uint8_t             uTagRequirement,
+                               int64_t            *pnTime)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   ProcessEpochDate(pMe, &Item, uTagRequirement, pnTime);
+}
+
+
+void
+QCBORDecode_GetEpochDateInMapSZ(QCBORDecodeContext *pMe,
+                                const char         *szLabel,
+                                uint8_t             uTagRequirement,
+                                int64_t            *pnTime)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   ProcessEpochDate(pMe, &Item, uTagRequirement, pnTime);
+}
+
+
+
+
+void QCBORDecode_GetTaggedStringInternal(QCBORDecodeContext *pMe,
+                                         TagSpecification    TagSpec,
+                                         UsefulBufC         *pBstr)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state, do nothing
+      return;
+   }
+
+   QCBORError uError;
+   QCBORItem  Item;
+
+   uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError != QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      *pBstr = Item.val.string;
+   } else {
+      *pBstr = NULLUsefulBufC;
+   }
+}
+
+
+
+
+static QCBORError ProcessBigNum(uint8_t          uTagRequirement,
+                                const QCBORItem *pItem,
+                                UsefulBufC      *pValue,
+                                bool            *pbIsNegative)
+{
+   const TagSpecification 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 = CheckTagRequirement(TagSpec, pItem);
+   if(uErr != QCBOR_SUCCESS) {
+      return uErr;
+   }
+
+   *pValue = pItem->val.string;
+
+   if(pItem->uDataType == QCBOR_TYPE_POSBIGNUM) {
+      *pbIsNegative = false;
+   } else if(pItem->uDataType == QCBOR_TYPE_NEGBIGNUM) {
+      *pbIsNegative = true;
+   }
+
+   return QCBOR_SUCCESS;
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h
+ */
+void QCBORDecode_GetBignum(QCBORDecodeContext *pMe,
+                           uint8_t             uTagRequirement,
+                           UsefulBufC         *pValue,
+                           bool               *pbIsNegative)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      // Already in error state, do nothing
+      return;
+   }
+
+   QCBORItem  Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError != QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ProcessBigNum(uTagRequirement, &Item, pValue, pbIsNegative);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h
+*/
+void QCBORDecode_GetBignumInMapN(QCBORDecodeContext *pMe,
+                                 int64_t             nLabel,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pValue,
+                                 bool               *pbIsNegative)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ProcessBigNum(uTagRequirement, &Item, pValue, pbIsNegative);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h
+*/
+void QCBORDecode_GetBignumInMapSZ(QCBORDecodeContext *pMe,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  UsefulBufC         *pValue,
+                                  bool               *pbIsNegative)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ProcessBigNum(uTagRequirement, &Item, pValue, pbIsNegative);
+}
+
+
+
+
+// Semi private
+QCBORError QCBORDecode_GetMIMEInternal(uint8_t     uTagRequirement,
+                                       const       QCBORItem *pItem,
+                                       UsefulBufC *pMessage,
+                                       bool       *pbIsTag257)
+{
+   const TagSpecification 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 TagSpecification 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(CheckTagRequirement(TagSpecText, pItem) == QCBOR_SUCCESS) {
+      *pMessage = pItem->val.string;
+      if(pbIsTag257 != NULL) {
+         *pbIsTag257 = false;
+      }
+      uReturn = QCBOR_SUCCESS;
+   } else if(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
+
+
+
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+
+typedef QCBORError (*fExponentiator)(uint64_t uMantissa, int64_t nExponent, uint64_t *puResult);
+
+
+// The exponentiator that works on only positive numbers
+static QCBORError
+Exponentitate10(uint64_t uMantissa, int64_t nExponent, uint64_t *puResult)
+{
+   uint64_t uResult = uMantissa;
+
+   if(uResult != 0) {
+      /* This loop will run a maximum of 19 times because
+       * UINT64_MAX < 10 ^^ 19. More than that will cause
+       * exit with the overflow error
+       */
+      for(; nExponent > 0; nExponent--) {
+         if(uResult > UINT64_MAX / 10) {
+            return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Error overflow
+         }
+         uResult = uResult * 10;
+      }
+
+      for(; nExponent < 0; nExponent++) {
+         uResult = uResult / 10;
+         if(uResult == 0) {
+            return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Underflow error
+         }
+      }
+   }
+   /* else, mantissa is zero so this returns zero */
+
+   *puResult = uResult;
+
+   return QCBOR_SUCCESS;
+}
+
+
+// The exponentiator that works on only positive numbers
+static QCBORError
+Exponentitate2(uint64_t uMantissa, int64_t nExponent, uint64_t *puResult)
+{
+   uint64_t uResult;
+
+   uResult = uMantissa;
+
+   /* This loop will run a maximum of 64 times because
+    * INT64_MAX < 2^31. More than that will cause
+    * exit with the overflow error
+    */
+   while(nExponent > 0) {
+      if(uResult > UINT64_MAX >> 1) {
+         return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Error overflow
+      }
+      uResult = uResult << 1;
+      nExponent--;
+   }
+
+   while(nExponent < 0 ) {
+      if(uResult == 0) {
+         return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Underflow error
+      }
+      uResult = uResult >> 1;
+      nExponent++;
+   }
+
+   *puResult = uResult;
+
+   return QCBOR_SUCCESS;
+}
+
+
+/*
+ Compute value with signed mantissa and signed result. Works with
+ exponent of 2 or 10 based on exponentiator.
+ */
+static inline QCBORError ExponentiateNN(int64_t       nMantissa,
+                                        int64_t       nExponent,
+                                        int64_t       *pnResult,
+                                        fExponentiator pfExp)
+{
+   uint64_t uResult;
+
+   // Take the absolute value of the mantissa and convert to unsigned.
+   // Improvement: this should be possible in one instruction
+   uint64_t uMantissa = nMantissa > 0 ? (uint64_t)nMantissa : (uint64_t)-nMantissa;
+
+   // Do the exponentiation of the positive mantissa
+   QCBORError uReturn = (*pfExp)(uMantissa, nExponent, &uResult);
+   if(uReturn) {
+      return uReturn;
+   }
+
+
+   /* (uint64_t)INT64_MAX+1 is used to represent the absolute value
+    of INT64_MIN. This assumes two's compliment representation where
+    INT64_MIN is one increment farther from 0 than INT64_MAX.
+    Trying to write -INT64_MIN doesn't work to get this because the
+    compiler tries to work with an int64_t which can't represent
+    -INT64_MIN.
+    */
+   uint64_t uMax = nMantissa > 0 ? INT64_MAX : (uint64_t)INT64_MAX+1;
+
+   // Error out if too large
+   if(uResult > uMax) {
+      return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+   }
+
+   // Casts are safe because of checks above
+   *pnResult = nMantissa > 0 ? (int64_t)uResult : -(int64_t)uResult;
+
+   return QCBOR_SUCCESS;
+}
+
+
+/*
+ Compute value with signed mantissa and unsigned result. Works with
+ exponent of 2 or 10 based on exponentiator.
+ */
+static inline QCBORError ExponentitateNU(int64_t       nMantissa,
+                                         int64_t       nExponent,
+                                         uint64_t      *puResult,
+                                         fExponentiator pfExp)
+{
+   if(nMantissa < 0) {
+      return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+   }
+
+   // Cast to unsigned is OK because of check for negative
+   // Cast to unsigned is OK because UINT64_MAX > INT64_MAX
+   // Exponentiation is straight forward
+   return (*pfExp)((uint64_t)nMantissa, nExponent, puResult);
+}
+
+
+/*
+ Compute value with signed mantissa and unsigned result. Works with
+ exponent of 2 or 10 based on exponentiator.
+ */
+static inline QCBORError ExponentitateUU(uint64_t       uMantissa,
+                                         int64_t        nExponent,
+                                         uint64_t      *puResult,
+                                         fExponentiator pfExp)
+{
+   return (*pfExp)(uMantissa, nExponent, puResult);
+}
+
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+
+
+
+
+static QCBORError ConvertBigNumToUnsigned(const UsefulBufC BigNum, uint64_t uMax, uint64_t *pResult)
+{
+   uint64_t uResult;
+
+   uResult = 0;
+   const uint8_t *pByte = BigNum.ptr;
+   size_t uLen = BigNum.len;
+   while(uLen--) {
+      if(uResult > (uMax >> 8)) {
+         return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+      }
+      uResult = (uResult << 8) + *pByte++;
+   }
+
+   *pResult = uResult;
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError ConvertPositiveBigNumToUnsigned(const UsefulBufC BigNum, uint64_t *pResult)
+{
+   return ConvertBigNumToUnsigned(BigNum, UINT64_MAX, pResult);
+}
+
+
+static inline QCBORError ConvertPositiveBigNumToSigned(const UsefulBufC BigNum, int64_t *pResult)
+{
+   uint64_t uResult;
+   QCBORError uError =  ConvertBigNumToUnsigned(BigNum, INT64_MAX, &uResult);
+   if(uError) {
+      return uError;
+   }
+   /* Cast is safe because ConvertBigNum is told to limit to INT64_MAX */
+   *pResult = (int64_t)uResult;
+   return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError ConvertNegativeBigNumToSigned(const UsefulBufC BigNum, int64_t *pnResult)
+{
+   uint64_t uResult;
+   /* The negative integer furthest from zero for a C int64_t is
+    INT64_MIN which is expressed as -INT64_MAX - 1. The value of a
+    negative number in CBOR is computed as -n - 1 where n is the
+    encoded integer, where n is what is in the variable BigNum. When
+    converting BigNum to a uint64_t, the maximum value is thus
+    INT64_MAX, so that when it -n - 1 is applied to it the result will
+    never be further from 0 than INT64_MIN.
+
+       -n - 1 <= INT64_MIN.
+       -n - 1 <= -INT64_MAX - 1
+        n     <= INT64_MAX.
+    */
+   QCBORError uError = ConvertBigNumToUnsigned(BigNum, INT64_MAX, &uResult);
+   if(uError != QCBOR_SUCCESS) {
+      return uError;
+   }
+
+   /// Now apply -n - 1. The cast is safe because
+   // ConvertBigNumToUnsigned() is limited to INT64_MAX which does fit
+   // is the largest positive integer that an int64_t can
+   // represent. */
+   *pnResult =  -(int64_t)uResult - 1;
+
+   return QCBOR_SUCCESS;
+}
+
+
+
+
+
+/*
+Convert integers and floats to an int64_t.
+
+\param[in] uConvertTypes  Bit mask list of conversion options.
+
+\retval QCBOR_ERR_UNEXPECTED_TYPE  Conversion, possible, but not requested
+                                   in uConvertTypes.
+
+\retval QCBOR_ERR_UNEXPECTED_TYPE  Of a type that can't be converted
+
+\retval QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW  Conversion result is too large
+                                              or too small.
+*/
+static QCBORError
+ConvertInt64(const QCBORItem *pItem, uint32_t uConvertTypes, int64_t *pnValue)
+{
+   switch(pItem->uDataType) {
+      case QCBOR_TYPE_FLOAT:
+      case QCBOR_TYPE_DOUBLE:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
+            /* https://pubs.opengroup.org/onlinepubs/009695399/functions/llround.html
+             http://www.cplusplus.com/reference/cmath/llround/
+             */
+            // Not interested in FE_INEXACT
+            feclearexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO);
+            if(pItem->uDataType == QCBOR_TYPE_DOUBLE) {
+               *pnValue = llround(pItem->val.dfnum);
+            } else {
+               *pnValue = lroundf(pItem->val.fnum);
+            }
+            if(fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO)) {
+               // llround() shouldn't result in divide by zero, but catch
+               // it here in case it unexpectedly does.  Don't try to
+               // distinguish between the various exceptions because it seems
+               // they vary by CPU, compiler and OS.
+               return QCBOR_ERR_FLOAT_EXCEPTION;
+            }
+         } else {
+            return  QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+#else
+         return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+         break;
+
+      case QCBOR_TYPE_INT64:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
+            *pnValue = pItem->val.int64;
+         } else {
+            return  QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_UINT64:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
+            if(pItem->val.uint64 < INT64_MAX) {
+               *pnValue = pItem->val.int64;
+            } else {
+               return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+            }
+         } else {
+            return  QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      default:
+         return  QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+   return QCBOR_SUCCESS;
+}
+
+
+void QCBORDecode_GetInt64ConvertInternal(QCBORDecodeContext *pMe,
+                                         uint32_t            uConvertTypes,
+                                         int64_t            *pnValue,
+                                         QCBORItem          *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   if(pItem) {
+      *pItem = Item;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertInt64(&Item, uConvertTypes, pnValue);
+}
+
+
+void QCBORDecode_GetInt64ConvertInternalInMapN(QCBORDecodeContext *pMe,
+                                               int64_t             nLabel,
+                                               uint32_t            uConvertTypes,
+                                               int64_t            *pnValue,
+                                               QCBORItem          *pItem)
+{
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertInt64(pItem, uConvertTypes, pnValue);
+}
+
+
+void QCBORDecode_GetInt64ConvertInternalInMapSZ(QCBORDecodeContext *pMe,
+                                               const char *         szLabel,
+                                               uint32_t             uConvertTypes,
+                                               int64_t             *pnValue,
+                                               QCBORItem           *pItem)
+{
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertInt64(pItem, uConvertTypes, pnValue);
+}
+
+
+/*
+ Convert a large variety of integer types to an int64_t.
+
+ \param[in] uConvertTypes  Bit mask list of conversion options.
+
+ \retval QCBOR_ERR_UNEXPECTED_TYPE  Conversion, possible, but not requested
+                                    in uConvertTypes.
+
+ \retval QCBOR_ERR_UNEXPECTED_TYPE  Of a type that can't be converted
+
+ \retval QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW  Conversion result is too large
+                                               or too small.
+ */
+static QCBORError
+Int64ConvertAll(const QCBORItem *pItem, uint32_t uConvertTypes, int64_t *pnValue)
+{
+   switch(pItem->uDataType) {
+
+      case QCBOR_TYPE_POSBIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIG_NUM) {
+            return ConvertPositiveBigNumToSigned(pItem->val.bigNum, pnValue);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_NEGBIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIG_NUM) {
+            return ConvertNegativeBigNumToSigned(pItem->val.bigNum, pnValue);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      case QCBOR_TYPE_DECIMAL_FRACTION:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            return ExponentiateNN(pItem->val.expAndMantissa.Mantissa.nInt,
+                                  pItem->val.expAndMantissa.nExponent,
+                                  pnValue,
+                                 &Exponentitate10);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+            return ExponentiateNN(pItem->val.expAndMantissa.Mantissa.nInt,
+                                  pItem->val.expAndMantissa.nExponent,
+                                  pnValue,
+                                  Exponentitate2);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            int64_t    nMantissa;
+            QCBORError uErr;
+            uErr = ConvertPositiveBigNumToSigned(pItem->val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+            if(uErr) {
+               return uErr;
+            }
+            return ExponentiateNN(nMantissa,
+                                  pItem->val.expAndMantissa.nExponent,
+                                  pnValue,
+                                  Exponentitate10);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            int64_t    nMantissa;
+            QCBORError uErr;
+            uErr = ConvertNegativeBigNumToSigned(pItem->val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+            if(uErr) {
+               return uErr;
+            }
+            return ExponentiateNN(nMantissa,
+                                  pItem->val.expAndMantissa.nExponent,
+                                  pnValue,
+                                  Exponentitate10);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            int64_t    nMantissa;
+            QCBORError uErr;
+            uErr = ConvertPositiveBigNumToSigned(pItem->val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+            if(uErr) {
+               return uErr;
+            }
+            return ExponentiateNN(nMantissa,
+                                  pItem->val.expAndMantissa.nExponent,
+                                  pnValue,
+                                  Exponentitate2);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            int64_t    nMantissa;
+            QCBORError uErr;
+            uErr = ConvertNegativeBigNumToSigned(pItem->val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+            if(uErr) {
+               return uErr;
+            }
+            return ExponentiateNN(nMantissa,
+                                  pItem->val.expAndMantissa.nExponent,
+                                  pnValue,
+                                  Exponentitate2);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+
+      default:
+         return QCBOR_ERR_UNEXPECTED_TYPE;   }
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_GetInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uConvertTypes, int64_t *pnValue)
+{
+   QCBORItem Item;
+
+   QCBORDecode_GetInt64ConvertInternal(pMe, uConvertTypes, pnValue, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)Int64ConvertAll(&Item, uConvertTypes, pnValue);
+}
+
+
+/*
+Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetInt64ConvertAllInMapN(QCBORDecodeContext *pMe,
+                                          int64_t             nLabel,
+                                          uint32_t            uConvertTypes,
+                                          int64_t            *pnValue)
+{
+   QCBORItem Item;
+
+   QCBORDecode_GetInt64ConvertInternalInMapN(pMe,
+                                             nLabel,
+                                             uConvertTypes,
+                                             pnValue,
+                                             &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)Int64ConvertAll(&Item, uConvertTypes, pnValue);
+}
+
+
+/*
+Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetInt64ConvertAllInMapSZ(QCBORDecodeContext *pMe,
+                                           const char         *szLabel,
+                                           uint32_t            uConvertTypes,
+                                           int64_t            *pnValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetInt64ConvertInternalInMapSZ(pMe,
+                                              szLabel,
+                                              uConvertTypes,
+                                              pnValue,
+                                              &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)Int64ConvertAll(&Item, uConvertTypes, pnValue);
+}
+
+
+static QCBORError ConvertUInt64(const QCBORItem *pItem, uint32_t uConvertTypes, uint64_t *puValue)
+{
+   switch(pItem->uDataType) {
+      case QCBOR_TYPE_DOUBLE:
+      case QCBOR_TYPE_FLOAT:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
+            // Can't use llround here because it will not convert values
+            // greater than INT64_MAX and less than UINT64_MAX that
+            // need to be converted so it is more complicated.
+            feclearexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO);
+            if(pItem->uDataType == QCBOR_TYPE_DOUBLE) {
+               if(isnan(pItem->val.dfnum)) {
+                  return QCBOR_ERR_FLOAT_EXCEPTION;
+               } else if(pItem->val.dfnum < 0) {
+                  return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+               } else {
+                  double dRounded = round(pItem->val.dfnum);
+                  // See discussion in DecodeDateEpoch() for
+                  // explanation of - 0x7ff
+                  if(dRounded > (double)(UINT64_MAX- 0x7ff)) {
+                     return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+                  }
+                  *puValue = (uint64_t)dRounded;
+               }
+            } else {
+               if(isnan(pItem->val.fnum)) {
+                  return QCBOR_ERR_FLOAT_EXCEPTION;
+               } else if(pItem->val.fnum < 0) {
+                  return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+               } else {
+                  float fRounded = roundf(pItem->val.fnum);
+                  // See discussion in DecodeDateEpoch() for
+                  // explanation of - 0x7ff
+                  if(fRounded > (float)(UINT64_MAX- 0x7ff)) {
+                     return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+                  }
+                  *puValue = (uint64_t)fRounded;
+               }
+            }
+            if(fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO)) {
+               // round() and roundf() shouldn't result in exceptions here, but
+               // catch them to be robust and thorough. Don't try to
+               // distinguish between the various exceptions because it seems
+               // they vary by CPU, compiler and OS.
+               return QCBOR_ERR_FLOAT_EXCEPTION;
+            }
+
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+#else
+         return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+         break;
+
+      case QCBOR_TYPE_INT64:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
+            if(pItem->val.int64 >= 0) {
+               *puValue = (uint64_t)pItem->val.int64;
+            } else {
+               return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+            }
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_UINT64:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
+            *puValue =  pItem->val.uint64;
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      default:
+         return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   return QCBOR_SUCCESS;
+}
+
+
+void QCBORDecode_GetUInt64ConvertInternal(QCBORDecodeContext *pMe,
+                                          uint32_t uConvertTypes,
+                                          uint64_t *puValue,
+                                          QCBORItem *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   if(pItem) {
+      *pItem = Item;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertUInt64(&Item, uConvertTypes, puValue);
+}
+
+
+void QCBORDecode_GetUInt64ConvertInternalInMapN(QCBORDecodeContext *pMe,
+                                               int64_t             nLabel,
+                                               uint32_t            uConvertTypes,
+                                               uint64_t            *puValue,
+                                               QCBORItem          *pItem)
+{
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertUInt64(pItem, uConvertTypes, puValue);
+}
+
+
+void QCBORDecode_GetUInt64ConvertInternalInMapSZ(QCBORDecodeContext *pMe,
+                                               const char *         szLabel,
+                                               uint32_t             uConvertTypes,
+                                               uint64_t             *puValue,
+                                               QCBORItem           *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertUInt64(pItem, uConvertTypes, puValue);
+}
+
+
+
+static QCBORError
+UInt64ConvertAll(const QCBORItem *pItem, uint32_t uConvertTypes, uint64_t *puValue)
+{
+   switch(pItem->uDataType) {
+
+      case QCBOR_TYPE_POSBIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIG_NUM) {
+            return ConvertPositiveBigNumToUnsigned(pItem->val.bigNum, puValue);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_NEGBIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIG_NUM) {
+            return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+
+      case QCBOR_TYPE_DECIMAL_FRACTION:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            return ExponentitateNU(pItem->val.expAndMantissa.Mantissa.nInt,
+                                   pItem->val.expAndMantissa.nExponent,
+                                   puValue,
+                                   Exponentitate10);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+            return ExponentitateNU(pItem->val.expAndMantissa.Mantissa.nInt,
+                                   pItem->val.expAndMantissa.nExponent,
+                                   puValue,
+                                   Exponentitate2);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            uint64_t   uMantissa;
+            QCBORError uErr;
+            uErr = ConvertPositiveBigNumToUnsigned(pItem->val.expAndMantissa.Mantissa.bigNum, &uMantissa);
+            if(uErr != QCBOR_SUCCESS) {
+               return uErr;
+            }
+            return ExponentitateUU(uMantissa,
+                                   pItem->val.expAndMantissa.nExponent,
+                                   puValue,
+                                   Exponentitate10);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            uint64_t   uMantissa;
+            QCBORError uErr;
+            uErr =  ConvertPositiveBigNumToUnsigned(pItem->val.expAndMantissa.Mantissa.bigNum, &uMantissa);
+            if(uErr != QCBOR_SUCCESS) {
+               return uErr;
+            }
+            return ExponentitateUU(uMantissa,
+                                   pItem->val.expAndMantissa.nExponent,
+                                   puValue,
+                                   Exponentitate2);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+#endif
+      default:
+         return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+ */
+void QCBORDecode_GetUInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uConvertTypes, uint64_t *puValue)
+{
+   QCBORItem Item;
+
+   QCBORDecode_GetUInt64ConvertInternal(pMe, uConvertTypes, puValue, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)UInt64ConvertAll(&Item, uConvertTypes, puValue);
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetUInt64ConvertAllInMapN(QCBORDecodeContext *pMe,
+                                           int64_t             nLabel,
+                                           uint32_t            uConvertTypes,
+                                           uint64_t           *puValue)
+{
+   QCBORItem Item;
+
+   QCBORDecode_GetUInt64ConvertInternalInMapN(pMe,
+                                              nLabel,
+                                              uConvertTypes,
+                                              puValue,
+                                              &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)UInt64ConvertAll(&Item, uConvertTypes, puValue);
+}
+
+
+/*
+  Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetUInt64ConvertAllInMapSZ(QCBORDecodeContext *pMe,
+                                            const char         *szLabel,
+                                            uint32_t            uConvertTypes,
+                                            uint64_t           *puValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetUInt64ConvertInternalInMapSZ(pMe,
+                                               szLabel,
+                                               uConvertTypes,
+                                               puValue,
+                                               &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)UInt64ConvertAll(&Item, uConvertTypes, puValue);
+}
+
+
+
+
+static QCBORError ConvertDouble(const QCBORItem *pItem,
+                                uint32_t         uConvertTypes,
+                                double          *pdValue)
+{
+   switch(pItem->uDataType) {
+      case QCBOR_TYPE_FLOAT:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
+            if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
+               // Simple cast does the job.
+               *pdValue = (double)pItem->val.fnum;
+            } else {
+               return QCBOR_ERR_UNEXPECTED_TYPE;
+            }
+         }
+#else
+         return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif
+         break;
+
+      case QCBOR_TYPE_DOUBLE:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
+            if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
+               *pdValue = pItem->val.dfnum;
+            } else {
+               return QCBOR_ERR_UNEXPECTED_TYPE;
+            }
+         }
+         break;
+
+      case QCBOR_TYPE_INT64:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
+            // A simple cast seems to do the job with no worry of exceptions.
+            // There will be precision loss for some values.
+            *pdValue = (double)pItem->val.int64;
+
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+#else
+         return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+         break;
+
+      case QCBOR_TYPE_UINT64:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
+            // A simple cast seems to do the job with no worry of exceptions.
+            // There will be precision loss for some values.
+            *pdValue = (double)pItem->val.uint64;
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+#else
+         return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+      default:
+         return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   return QCBOR_SUCCESS;
+}
+
+
+void QCBORDecode_GetDoubleConvertInternal(QCBORDecodeContext *pMe,
+                                          uint32_t            uConvertTypes,
+                                          double             *pdValue,
+                                          QCBORItem          *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   if(pItem) {
+      *pItem = Item;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertDouble(&Item, uConvertTypes, pdValue);
+}
+
+
+void QCBORDecode_GetDoubleConvertInternalInMapN(QCBORDecodeContext *pMe,
+                                               int64_t             nLabel,
+                                               uint32_t            uConvertTypes,
+                                               double             *pdValue,
+                                               QCBORItem          *pItem)
+{
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertDouble(pItem, uConvertTypes, pdValue);
+}
+
+
+void QCBORDecode_GetDoubleConvertInternalInMapSZ(QCBORDecodeContext *pMe,
+                                               const char *          szLabel,
+                                               uint32_t              uConvertTypes,
+                                               double               *pdValue,
+                                               QCBORItem            *pItem)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, pItem);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)ConvertDouble(pItem, uConvertTypes, pdValue);
+}
+
+
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+static double ConvertBigNumToDouble(const UsefulBufC BigNum)
+{
+   double dResult;
+
+   dResult = 0.0;
+   const uint8_t *pByte = BigNum.ptr;
+   size_t uLen = BigNum.len;
+   /* This will overflow and become the float value INFINITY if the number
+    is too large to fit. */
+   while(uLen--) {
+      dResult = (dResult * 256.0) + (double)*pByte++;
+   }
+
+   return dResult;
+}
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+
+static QCBORError
+DoubleConvertAll(const QCBORItem *pItem, uint32_t uConvertTypes, double *pdValue)
+{
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   /*
+   https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
+
+   */
+   switch(pItem->uDataType) {
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      case QCBOR_TYPE_DECIMAL_FRACTION:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            // Underflow gives 0, overflow gives infinity
+            *pdValue = (double)pItem->val.expAndMantissa.Mantissa.nInt *
+                        pow(10.0, (double)pItem->val.expAndMantissa.nExponent);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIGFLOAT ) {
+            // Underflow gives 0, overflow gives infinity
+            *pdValue = (double)pItem->val.expAndMantissa.Mantissa.nInt *
+                              exp2((double)pItem->val.expAndMantissa.nExponent);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+#endif /* ndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+      case QCBOR_TYPE_POSBIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIG_NUM) {
+            *pdValue = ConvertBigNumToDouble(pItem->val.bigNum);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_NEGBIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_BIG_NUM) {
+            *pdValue = -1-ConvertBigNumToDouble(pItem->val.bigNum);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+         if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+            double dMantissa = ConvertBigNumToDouble(pItem->val.expAndMantissa.Mantissa.bigNum);
+            *pdValue = dMantissa * pow(10, (double)pItem->val.expAndMantissa.nExponent);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+        if(uConvertTypes & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+         double dMantissa = -ConvertBigNumToDouble(pItem->val.expAndMantissa.Mantissa.bigNum);
+         *pdValue = dMantissa * pow(10, (double)pItem->val.expAndMantissa.nExponent);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+        if(uConvertTypes & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+         double dMantissa = ConvertBigNumToDouble(pItem->val.expAndMantissa.Mantissa.bigNum);
+         *pdValue = dMantissa * exp2((double)pItem->val.expAndMantissa.nExponent);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+
+      case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+        if(uConvertTypes & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+         double dMantissa = -1-ConvertBigNumToDouble(pItem->val.expAndMantissa.Mantissa.bigNum);
+         *pdValue = dMantissa * exp2((double)pItem->val.expAndMantissa.nExponent);
+         } else {
+            return QCBOR_ERR_UNEXPECTED_TYPE;
+         }
+         break;
+#endif /* ndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+      default:
+         return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   return QCBOR_SUCCESS;
+
+#else
+   (void)pItem;
+   (void)uConvertTypes;
+   (void)pdValue;
+   return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+}
+
+
+/*
+   Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDoubleConvertAll(QCBORDecodeContext *pMe,
+                                     uint32_t           uConvertTypes,
+                                     double *pdValue)
+{
+
+   QCBORItem Item;
+
+   QCBORDecode_GetDoubleConvertInternal(pMe, uConvertTypes, pdValue, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)DoubleConvertAll(&Item, uConvertTypes, pdValue);
+}
+
+
+/*
+   Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDoubleConvertAllInMapN(QCBORDecodeContext *pMe,
+                                           int64_t             nLabel,
+                                           uint32_t            uConvertTypes,
+                                           double             *pdValue)
+{
+   QCBORItem Item;
+
+   QCBORDecode_GetDoubleConvertInternalInMapN(pMe, nLabel, uConvertTypes, pdValue, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)DoubleConvertAll(&Item, uConvertTypes, pdValue);
+}
+
+
+/*
+   Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDoubleConvertAllInMapSZ(QCBORDecodeContext *pMe,
+                                            const char         *szLabel,
+                                            uint32_t            uConvertTypes,
+                                            double             *pdValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetDoubleConvertInternalInMapSZ(pMe, szLabel, uConvertTypes, pdValue, &Item);
+
+   if(pMe->uLastError == QCBOR_SUCCESS) {
+      // The above conversion succeeded
+      return;
+   }
+
+   if(pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+      // The above conversion failed in a way that code below can't correct
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)DoubleConvertAll(&Item, uConvertTypes, pdValue);
+}
+
+
+
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+static inline UsefulBufC ConvertIntToBigNum(uint64_t uInt, UsefulBuf Buffer)
+{
+   while((uInt & 0xff00000000000000UL) == 0) {
+      uInt = uInt << 8;
+   };
+
+   UsefulOutBuf UOB;
+
+   UsefulOutBuf_Init(&UOB, Buffer);
+
+   while(uInt) {
+      const uint64_t xx = uInt & 0xff00000000000000UL;
+      UsefulOutBuf_AppendByte(&UOB, (uint8_t)((uInt & 0xff00000000000000UL) >> 56));
+      uInt = uInt << 8;
+      (void)xx;
+   }
+
+   return UsefulOutBuf_OutUBuf(&UOB);
+}
+
+
+static QCBORError MantissaAndExponentTypeHandler(QCBORDecodeContext *pMe,
+                                                 TagSpecification    TagSpec,
+                                                 QCBORItem          *pItem)
+{
+   QCBORError uErr;
+   // Loops runs at most 1.5 times. Making it a loop saves object code.
+   while(1) {
+      uErr = CheckTagRequirement(TagSpec, pItem);
+      if(uErr != QCBOR_SUCCESS) {
+         goto Done;
+      }
+
+      if(pItem->uDataType != QCBOR_TYPE_ARRAY) {
+         break; // Successful exit. Moving on to finish decoding.
+      }
+
+      // The item is an array, which means an undecoded
+      // mantissa and exponent, so decode it. It will then
+      // have a different type and exit the loop if.
+      uErr = QCBORDecode_MantissaAndExponent(pMe, pItem);
+      if(uErr != QCBOR_SUCCESS) {
+         goto Done;
+      }
+
+      // Second time around, the type must match.
+      TagSpec.uTagRequirement = QCBOR_TAG_REQUIREMENT_TAG;
+   }
+Done:
+   return uErr;
+}
+
+
+static void ProcessMantissaAndExponent(QCBORDecodeContext *pMe,
+                                       TagSpecification    TagSpec,
+                                       QCBORItem          *pItem,
+                                       int64_t            *pnMantissa,
+                                       int64_t            *pnExponent)
+{
+   QCBORError uErr;
+
+   uErr = MantissaAndExponentTypeHandler(pMe, TagSpec, pItem);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   switch (pItem->uDataType) {
+
+      case QCBOR_TYPE_DECIMAL_FRACTION:
+      case QCBOR_TYPE_BIGFLOAT:
+         *pnMantissa = pItem->val.expAndMantissa.Mantissa.nInt;
+         *pnExponent = pItem->val.expAndMantissa.nExponent;
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+      case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+         *pnExponent = pItem->val.expAndMantissa.nExponent;
+         uErr = ConvertPositiveBigNumToSigned(pItem->val.expAndMantissa.Mantissa.bigNum, pnMantissa);
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+      case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+         *pnExponent = pItem->val.expAndMantissa.nExponent;
+         uErr = ConvertNegativeBigNumToSigned(pItem->val.expAndMantissa.Mantissa.bigNum, pnMantissa);
+         break;
+
+      default:
+         uErr = QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   Done:
+      pMe->uLastError = (uint8_t)uErr;
+}
+
+
+static void ProcessMantissaAndExponentBig(QCBORDecodeContext *pMe,
+                                          TagSpecification    TagSpec,
+                                          QCBORItem          *pItem,
+                                          UsefulBuf           BufferForMantissa,
+                                          UsefulBufC         *pMantissa,
+                                          bool               *pbIsNegative,
+                                          int64_t            *pnExponent)
+{
+   QCBORError uErr;
+
+   uErr = MantissaAndExponentTypeHandler(pMe, TagSpec, pItem);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   uint64_t uMantissa;
+
+   switch (pItem->uDataType) {
+
+      case QCBOR_TYPE_DECIMAL_FRACTION:
+      case QCBOR_TYPE_BIGFLOAT:
+         if(pItem->val.expAndMantissa.Mantissa.nInt >= 0) {
+            uMantissa = (uint64_t)pItem->val.expAndMantissa.Mantissa.nInt;
+            *pbIsNegative = false;
+         } else {
+            uMantissa = (uint64_t)-pItem->val.expAndMantissa.Mantissa.nInt;
+            *pbIsNegative = true;
+         }
+         *pMantissa = ConvertIntToBigNum(uMantissa, BufferForMantissa);
+         *pnExponent = pItem->val.expAndMantissa.nExponent;
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+      case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+         *pnExponent = pItem->val.expAndMantissa.nExponent;
+         *pMantissa = pItem->val.expAndMantissa.Mantissa.bigNum;
+         *pbIsNegative = false;
+         break;
+
+      case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+      case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+         *pnExponent = pItem->val.expAndMantissa.nExponent;
+         *pMantissa = pItem->val.expAndMantissa.Mantissa.bigNum;
+         *pbIsNegative = true;
+         break;
+
+      default:
+         uErr = QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+Done:
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDecimalFraction(QCBORDecodeContext *pMe,
+                                    uint8_t             uTagRequirement,
+                                    int64_t             *pnMantissa,
+                                    int64_t             *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponent(pMe, TagSpec, &Item, pnMantissa, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDecimalFractionInMapN(QCBORDecodeContext *pMe,
+                                          int64_t             nLabel,
+                                          uint8_t             uTagRequirement,
+                                          int64_t             *pnMantissa,
+                                          int64_t             *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponent(pMe, TagSpec, &Item, pnMantissa, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDecimalFractionInMapSZ(QCBORDecodeContext *pMe,
+                                           const char         *szLabel,
+                                           uint8_t             uTagRequirement,
+                                           int64_t             *pnMantissa,
+                                           int64_t             *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponent(pMe, TagSpec, &Item, pnMantissa, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDecimalFractionBig(QCBORDecodeContext *pMe,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBuf          MantissaBuffer,
+                                       UsefulBufC         *pMantissa,
+                                       bool               *pbMantissaIsNegative,
+                                       int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponentBig(pMe,
+                                 TagSpec,
+                                 &Item,
+                                 MantissaBuffer,
+                                 pMantissa,
+                                 pbMantissaIsNegative,
+                                 pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDecimalFractionBigInMapN(QCBORDecodeContext *pMe,
+                                             int64_t             nLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBuf           BufferForMantissa,
+                                             UsefulBufC         *pMantissa,
+                                             bool               *pbIsNegative,
+                                             int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponentBig(pMe,
+                                 TagSpec,
+                                 &Item,
+                                 BufferForMantissa,
+                                 pMantissa,
+                                 pbIsNegative,
+                                 pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetDecimalFractionBigInMapSZ(QCBORDecodeContext *pMe,
+                                              const char         *szLabel,
+                                              uint8_t             uTagRequirement,
+                                              UsefulBuf           BufferForMantissa,
+                                              UsefulBufC         *pMantissa,
+                                              bool               *pbIsNegative,
+                                              int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponentBig(pMe, TagSpec, &Item, BufferForMantissa, pMantissa, pbIsNegative, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBigFloat(QCBORDecodeContext *pMe,
+                             uint8_t             uTagRequirement,
+                             int64_t             *pnMantissa,
+                             int64_t             *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponent(pMe, TagSpec, &Item, pnMantissa, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBigFloatInMapN(QCBORDecodeContext *pMe,
+                                   int64_t             nLabel,
+                                   uint8_t             uTagRequirement,
+                                   int64_t            *pnMantissa,
+                                   int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponent(pMe, TagSpec, &Item, pnMantissa, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBigFloatInMapSZ(QCBORDecodeContext *pMe,
+                                    const char         *szLabel,
+                                    uint8_t             uTagRequirement,
+                                    int64_t            *pnMantissa,
+                                    int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponent(pMe, TagSpec, &Item, pnMantissa, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBigFloatBig(QCBORDecodeContext *pMe,
+                                uint8_t             uTagRequirement,
+                                UsefulBuf          MantissaBuffer,
+                                UsefulBufC         *pMantissa,
+                                bool               *pbMantissaIsNegative,
+                                int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponentBig(pMe, TagSpec, &Item, MantissaBuffer, pMantissa, pbMantissaIsNegative, pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBigFloatBigInMapN(QCBORDecodeContext *pMe,
+                                      int64_t             nLabel,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBuf           BufferForMantissa,
+                                      UsefulBufC         *pMantissa,
+                                      bool               *pbIsNegative,
+                                      int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponentBig(pMe,
+                                 TagSpec,
+                                 &Item,
+                                 BufferForMantissa,
+                                 pMantissa,
+                                 pbIsNegative,
+                                 pnExponent);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+*/
+void QCBORDecode_GetBigFloatBigInMapSZ(QCBORDecodeContext *pMe,
+                                       const char         *szLabel,
+                                       uint8_t             uTagRequirement,
+                                       UsefulBuf           BufferForMantissa,
+                                       UsefulBufC         *pMantissa,
+                                       bool               *pbIsNegative,
+                                       int64_t            *pnExponent)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   const TagSpecification 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}
+   };
+
+   ProcessMantissaAndExponentBig(pMe,
+                                 TagSpec,
+                                 &Item,
+                                 BufferForMantissa,
+                                 pMantissa,
+                                 pbIsNegative,
+                                 pnExponent);
+}
+
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 08de596..2416662 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -203,7 +203,7 @@
    QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN -- Finish called without enough closes
 
  Would generate not-well-formed CBOR
-   QCBOR_ERR_UNSUPPORTED             -- Simple type between 24 and 31
+   QCBOR_ERR_ENCODE_UNSUPPORTED      -- Simple type between 24 and 31
  */
 
 
@@ -565,7 +565,7 @@
 {
    if(me->uError == QCBOR_SUCCESS) {
       if(uNum >= CBOR_SIMPLEV_RESERVED_START && uNum <= CBOR_SIMPLEV_RESERVED_END) {
-         me->uError = QCBOR_ERR_UNSUPPORTED;
+         me->uError = QCBOR_ERR_ENCODE_UNSUPPORTED;
       } else {
          // AppendHead() does endian swapping for the float / double
          AppendCBORHead(me, CBOR_MAJOR_TYPE_SIMPLE, uNum, uMinLen);
@@ -633,6 +633,10 @@
  one of the inline wrappers will usually be called rather than this.
 
  See qcbor/qcbor_encode.h
+
+ Improvement: create another version of this that only
+ takes a big number mantissa and converts the output to
+ a type 0 or 1 integer when mantissa is small enough.
  */
 void QCBOREncode_AddExponentAndMantissa(QCBOREncodeContext *pMe,
                                         uint64_t            uTag,
@@ -648,7 +652,9 @@
     base-2 for big floats and base-10 for decimal fractions, but that
     has no effect on the code here.
     */
-   QCBOREncode_AddTag(pMe, uTag);
+   if(uTag != CBOR_TAG_INVALID64) {
+      QCBOREncode_AddTag(pMe, uTag);
+   }
    QCBOREncode_OpenArray(pMe);
    QCBOREncode_AddInt64(pMe, nExponent);
    if(!UsefulBuf_IsNULLC(BigNumMantissa)) {
diff --git a/src/qcbor_err_to_str.c b/src/qcbor_err_to_str.c
index 3f9db0a..e96ed70 100644
--- a/src/qcbor_err_to_str.c
+++ b/src/qcbor_err_to_str.c
@@ -2,6 +2,7 @@
  err_to_str.c -- strings names for errors
 
  Copyright (c) 2020, Patrick Uiterwijk. All rights reserved.
+ Copyright (c) 2020, Laurence Lundblade.
 
  SPDX-License-Identifier: BSD-3-Clause
 
@@ -10,39 +11,57 @@
  Created on 3/21/20
  =============================================================================*/
 
-#include "qcbor.h"
+#include "qcbor/qcbor_common.h"
 
 #define _ERR_TO_STR(errpart) case QCBOR_##errpart: return "QCBOR_" #errpart;
 
 const char *qcbor_err_to_str(QCBORError err) {
-	switch (err) {
-	_ERR_TO_STR(SUCCESS)
-	_ERR_TO_STR(ERR_BUFFER_TOO_SMALL)
-	_ERR_TO_STR(ERR_ARRAY_NESTING_TOO_DEEP)
-	_ERR_TO_STR(ERR_ARRAY_TOO_LONG)
-	_ERR_TO_STR(ERR_TOO_MANY_CLOSES)
-	_ERR_TO_STR(ERR_UNSUPPORTED)
-	_ERR_TO_STR(ERR_HIT_END)
-	_ERR_TO_STR(ERR_BUFFER_TOO_LARGE)
-	_ERR_TO_STR(ERR_INT_OVERFLOW)
-	_ERR_TO_STR(ERR_MAP_LABEL_TYPE)
-	_ERR_TO_STR(ERR_ARRAY_OR_MAP_STILL_OPEN)
-	_ERR_TO_STR(ERR_DATE_OVERFLOW)
-	_ERR_TO_STR(ERR_BAD_TYPE_7)
-	_ERR_TO_STR(ERR_BAD_OPT_TAG)
-	_ERR_TO_STR(ERR_EXTRA_BYTES)
-	_ERR_TO_STR(ERR_CLOSE_MISMATCH)
-	_ERR_TO_STR(ERR_NO_STRING_ALLOCATOR)
-	_ERR_TO_STR(ERR_INDEFINITE_STRING_CHUNK)
-	_ERR_TO_STR(ERR_STRING_ALLOCATE)
-	_ERR_TO_STR(ERR_BAD_BREAK)
-	_ERR_TO_STR(ERR_TOO_MANY_TAGS)
-	_ERR_TO_STR(ERR_BAD_INT)
-	_ERR_TO_STR(ERR_NO_MORE_ITEMS)
-	_ERR_TO_STR(ERR_BAD_EXP_AND_MANTISSA)
-	_ERR_TO_STR(ERR_STRING_TOO_LONG)
+    switch (err) {
+    _ERR_TO_STR(SUCCESS)
+    _ERR_TO_STR(ERR_BUFFER_TOO_SMALL)
+    _ERR_TO_STR(ERR_ENCODE_UNSUPPORTED)
+    _ERR_TO_STR(ERR_BUFFER_TOO_LARGE)
+    _ERR_TO_STR(ERR_ARRAY_NESTING_TOO_DEEP)
+    _ERR_TO_STR(ERR_CLOSE_MISMATCH)
+    _ERR_TO_STR(ERR_ARRAY_TOO_LONG)
+    _ERR_TO_STR(ERR_TOO_MANY_CLOSES)
+    _ERR_TO_STR(ERR_ARRAY_OR_MAP_STILL_OPEN)
+    _ERR_TO_STR(ERR_BAD_TYPE_7)
+    _ERR_TO_STR(ERR_EXTRA_BYTES)
+    _ERR_TO_STR(ERR_UNSUPPORTED)
+    _ERR_TO_STR(ERR_ARRAY_OR_MAP_UNCONSUMED)
+    _ERR_TO_STR(ERR_BAD_INT)
+    _ERR_TO_STR(ERR_INDEFINITE_STRING_CHUNK)
+    _ERR_TO_STR(ERR_HIT_END)
+    _ERR_TO_STR(ERR_BAD_BREAK)
+    _ERR_TO_STR(ERR_INPUT_TOO_LARGE)
+    _ERR_TO_STR(ERR_ARRAY_DECODE_NESTING_TOO_DEEP)
+    _ERR_TO_STR(ERR_ARRAY_DECODE_TOO_LONG)
+    _ERR_TO_STR(ERR_STRING_TOO_LONG)
+    _ERR_TO_STR(ERR_BAD_EXP_AND_MANTISSA)
+    _ERR_TO_STR(ERR_NO_STRING_ALLOCATOR)
+    _ERR_TO_STR(ERR_STRING_ALLOCATE)
+    _ERR_TO_STR(ERR_TOO_MANY_TAGS)
+    _ERR_TO_STR(ERR_MAP_LABEL_TYPE)
+    _ERR_TO_STR(ERR_UNEXPECTED_TYPE)
+    _ERR_TO_STR(ERR_BAD_OPT_TAG)
+    _ERR_TO_STR(ERR_DUPLICATE_LABEL)
+    _ERR_TO_STR(ERR_MEM_POOL_SIZE)
+    _ERR_TO_STR(ERR_INT_OVERFLOW)
+    _ERR_TO_STR(ERR_DATE_OVERFLOW)
+    _ERR_TO_STR(ERR_EXIT_MISMATCH)
+    _ERR_TO_STR(ERR_NO_MORE_ITEMS)
+    _ERR_TO_STR(ERR_LABEL_NOT_FOUND)
+    _ERR_TO_STR(ERR_NUMBER_SIGN_CONVERSION)
+    _ERR_TO_STR(ERR_CONVERSION_UNDER_OVER_FLOW)
+    _ERR_TO_STR(ERR_MAP_NOT_ENTERED)
+    _ERR_TO_STR(ERR_CALLBACK_FAIL)
+    _ERR_TO_STR(ERR_FLOAT_DATE_DISABLED)
+    _ERR_TO_STR(ERR_HALF_PRECISION_DISABLED)
+    _ERR_TO_STR(ERR_HW_FLOAT_DISABLED)
+    _ERR_TO_STR(ERR_FLOAT_EXCEPTION)
 
-	default:
-		return "Invalid error";
-	}
+    default:
+        return "Unidentified error";
+    }
 }
diff --git a/test/float_tests.c b/test/float_tests.c
index 5aefaea..3f8ec1a 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -14,6 +14,7 @@
 #include "float_tests.h"
 #include "qcbor/qcbor_encode.h"
 #include "qcbor/qcbor_decode.h"
+#include "qcbor/qcbor_spiffy_decode.h"
 #include <math.h> // For INFINITY and NAN and isnan()
 
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
@@ -604,7 +605,16 @@
 
 
 /*
-[0.0, 3.14, 0.0, NaN, Infinity, 0.0, 3.140000104904175, 0.0, NaN, Infinity,
+[0.0,  // Half
+ 3.14, // Double
+ 0.0,  // Double
+ NaN,  // Double
+ Infinity, // Double
+ 0.0,  // Half
+ 3.140000104904175, // Single
+ 0.0,  // Single
+ NaN,  // Single
+ Infinity, // Single
  {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
   102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
  */
@@ -726,7 +736,6 @@
 }
 
 
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
 /* returns 0 if equivalent, non-zero if not equivalent */
 static int CHECK_EXPECTED_DOUBLE(double val, double expected)
 {
@@ -740,7 +749,6 @@
       return 0;
    }
 }
-#endif
 
 
 int32_t GeneralFloatDecodeTests()
@@ -765,12 +773,12 @@
       Item.val.dfnum != 0.0) {
       return -2;
    }
-#else
+#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
    uErr = QCBORDecode_GetNext(&DC, &Item);
-   if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
+   if(uErr != QCBOR_ERR_HALF_PRECISION_DISABLED) {
       return -3;
    }
-#endif
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
 
    uErr = QCBORDecode_GetNext(&DC, &Item);
    if(uErr != QCBOR_SUCCESS ||
@@ -801,13 +809,22 @@
    }
 
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+   // Tests for normal config
    uErr = QCBORDecode_GetNext(&DC, &Item);
    if(uErr != QCBOR_SUCCESS ||
       Item.uDataType != QCBOR_TYPE_DOUBLE ||
       Item.val.dfnum != 0.0) {
       return -8;
    }
+#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
+      // Tests for preferred serialization turned off
+      uErr = QCBORDecode_GetNext(&DC, &Item);
+      if(uErr != QCBOR_ERR_HALF_PRECISION_DISABLED) {
+         return -13;
+      }
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
 
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
    uErr = QCBORDecode_GetNext(&DC, &Item);
    if(uErr != QCBOR_SUCCESS ||
       Item.uDataType != QCBOR_TYPE_DOUBLE ||
@@ -836,42 +853,289 @@
       return -12;
    }
 
-#else
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+   // Tests for floating point HW use disabled
    uErr = QCBORDecode_GetNext(&DC, &Item);
-   if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
-      return -13;
+   if(uErr != QCBOR_SUCCESS ||
+      Item.uDataType != QCBOR_TYPE_FLOAT ||
+      CHECK_EXPECTED_DOUBLE(3.14, Item.val.fnum)) {
+      return -9;
    }
 
    uErr = QCBORDecode_GetNext(&DC, &Item);
    if(uErr != QCBOR_SUCCESS ||
       Item.uDataType != QCBOR_TYPE_FLOAT ||
-      Item.val.fnum != 3.14f) {
-      return -14;
-   }
-
-   uErr = QCBORDecode_GetNext(&DC, &Item);
-   if(uErr != QCBOR_SUCCESS ||
-      Item.uDataType != QCBOR_TYPE_FLOAT ||
-      Item.val.fnum != 0.0f) {
-      return -15;
+      Item.val.fnum != 0.0) {
+      return -10;
    }
 
    uErr = QCBORDecode_GetNext(&DC, &Item);
    if(uErr != QCBOR_SUCCESS ||
       Item.uDataType != QCBOR_TYPE_FLOAT ||
       !isnan(Item.val.fnum)) {
-      return -16;
+      return -11;
    }
 
    uErr = QCBORDecode_GetNext(&DC, &Item);
    if(uErr != QCBOR_SUCCESS ||
       Item.uDataType != QCBOR_TYPE_FLOAT ||
       Item.val.fnum != INFINITY) {
-      return -17;
+      return -12;
    }
-#endif
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
    /* Sufficent test coverage. Don't need to decode the rest */
 
+
+   // Now tests for spiffy decode
+   TestData = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
+   double d;
+   QCBORDecode_Init(&DC, TestData, 0);
+   QCBORDecode_EnterArray(&DC, NULL);
+
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   // Spiffy decode tests for normal full float support
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -100;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 3.14) {
+      return -101;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -102;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      !isnan(d)) {
+      return -103;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != INFINITY) {
+      return -104;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -105;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 3.140000104904175) {
+      return -106;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -107;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      !isnan(d)) {
+      return -108;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != INFINITY) {
+      return -109;
+   }
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+   // Spiffy decode tests for float HW disabled
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -200;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 3.14) {
+      return -201;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -202;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      !isnan(d)) {
+      return -203;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != INFINITY) {
+      return -204;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_SUCCESS ||
+      d != 0.0) {
+      return -205;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_ERR_HW_FLOAT_DISABLED) {
+      return -206;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_ERR_HW_FLOAT_DISABLED) {
+      return -207;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_ERR_HW_FLOAT_DISABLED ) {
+      return -208;
+   }
+
+   QCBORDecode_GetDouble(&DC, &d);
+   uErr = QCBORDecode_GetAndResetError(&DC);
+   if(uErr != QCBOR_ERR_HW_FLOAT_DISABLED ) {
+      return -209;
+   }
+
+
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
+   // Spiffy decode tests for half-precision disabled
+   QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+    if(uErr != QCBOR_ERR_HALF_PRECISION_DISABLED) {
+       return -300;
+    }
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+    if(uErr != QCBOR_SUCCESS ||
+       d != 3.14) {
+       return -301;
+    }
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+    if(uErr != QCBOR_SUCCESS ||
+       d != 0.0) {
+       return -302;
+    }
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+    if(uErr != QCBOR_SUCCESS ||
+       !isnan(d)) {
+       return -303;
+    }
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+    if(uErr != QCBOR_SUCCESS ||
+       d != INFINITY) {
+       return -304;
+    }
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+    if(uErr != QCBOR_ERR_HALF_PRECISION_DISABLED) {
+       return -305;
+    }
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+    if(uErr != QCBOR_SUCCESS ||
+       d != 3.140000104904175) {
+       return -306;
+    }
+#else
+   // Disabled use of HW to convert from single to double
+   if(uErr != QCBOR_ERR_HW_FLOAT_DISABLED) {
+      return -316;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+    if(uErr != QCBOR_SUCCESS ||
+       d != 0.0) {
+       return -307;
+    }
+#else
+   // Disabled use of HW to convert from single to double
+   if(uErr == QCBOR_SUCCESS) {
+      return -317;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+    if(uErr != QCBOR_SUCCESS ||
+       !isnan(d)) {
+       return -308;
+    }
+#else
+   // Disabled use of HW to convert from single to double
+   if(uErr == QCBOR_SUCCESS) {
+      return -318;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+    QCBORDecode_GetDouble(&DC, &d);
+    uErr = QCBORDecode_GetAndResetError(&DC);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+    if(uErr != QCBOR_SUCCESS ||
+       d != INFINITY) {
+       return -309;
+    }
+#else
+   // Disabled use of HW to convert from single to double
+   if(uErr == QCBOR_SUCCESS) {
+      return -318;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+
    return 0;
 }
 
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index a14e9d2..cfa1450 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -33,10 +33,14 @@
 #include "qcbor_decode_tests.h"
 #include "qcbor/qcbor_encode.h"
 #include "qcbor/qcbor_decode.h"
+#include "qcbor/qcbor_spiffy_decode.h"
 #include <string.h>
 #include <math.h> // for fabs()
 #include "not_well_formed_cbor.h"
 
+// Handy macro to compare a UsefulBuf to a C string
+#define UsefulBufCompareToSZ(x, y) \
+   UsefulBuf_Compare(x, UsefulBuf_FromSZ(y))
 
 #ifdef  PRINT_FUNCTIONS_FOR_DEBUGGING
 #include <stdio.h>
@@ -538,16 +542,36 @@
    0x1e, 0x6c, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x6d,
    0x6e, 0x20, 0x6c, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64,
    0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63,
-   0x73 } ;
+   0x73 };
+
+// Same as above, but with indefinite lengths.
+static const uint8_t pValidMapIndefEncoded[] = {
+0xbf, 0x6d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x69, 0x6e,
+0x74, 0x65, 0x67, 0x65, 0x72, 0x18, 0x2a, 0x77, 0x61, 0x6e,
+0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20,
+0x74, 0x77, 0x6f, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+0x73, 0x9f, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31,
+0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x32, 0xff, 0x6c, 0x6d,
+0x61, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x6d, 0x61,
+0x70, 0xbf, 0x67, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x31,
+0x44, 0x78, 0x78, 0x78, 0x78, 0x67, 0x62, 0x79, 0x74, 0x65,
+0x73, 0x20, 0x32, 0x44, 0x79, 0x79, 0x79, 0x79, 0x6b, 0x61,
+0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x74,
+0x18, 0x62, 0x66, 0x74, 0x65, 0x78, 0x74, 0x20, 0x32, 0x78,
+0x1e, 0x6c, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x6d,
+0x6e, 0x20, 0x6c, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64,
+0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63,
+0x73, 0xff, 0xff};
+
 
 static int32_t ParseOrderedArray(const uint8_t *pEncoded,
-                             size_t nLen,
-                             int64_t *pInt1,
-                             int64_t *pInt2,
-                             const uint8_t **pBuf3,
-                             size_t *pBuf3Len,
-                             const uint8_t **pBuf4,
-                             size_t *pBuf4Len)
+                                 size_t nLen,
+                                 int64_t *pInt1,
+                                 int64_t *pInt2,
+                                 const uint8_t **pBuf3,
+                                 size_t *pBuf3Len,
+                                 const uint8_t **pBuf4,
+                                 size_t *pBuf4Len)
 {
    QCBORDecodeContext DCtx;
    QCBORItem          Item;
@@ -558,28 +582,38 @@
                     QCBOR_DECODE_MODE_NORMAL);
 
    // Make sure the first thing is a map
-   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_ARRAY)
+   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+      Item.uDataType != QCBOR_TYPE_ARRAY) {
       goto Done;
+   }
 
    // First integer
-   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_INT64)
+   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+      Item.uDataType != QCBOR_TYPE_INT64) {
       goto Done;
+   }
    *pInt1 = Item.val.int64;
 
    // Second integer
-   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_INT64)
+   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+      Item.uDataType != QCBOR_TYPE_INT64) {
       goto Done;
+   }
    *pInt2 = Item.val.int64;
 
    // First string
-   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_BYTE_STRING)
+   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+      Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
       goto Done;
+   }
    *pBuf3 = Item.val.string.ptr;
    *pBuf3Len = Item.val.string.len;
 
    // Second string
-   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 || Item.uDataType != QCBOR_TYPE_BYTE_STRING)
+   if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
+      Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
       goto Done;
+   }
    *pBuf4 = Item.val.string.ptr;
    *pBuf4Len = Item.val.string.len;
 
@@ -643,13 +677,43 @@
 static uint8_t sEmpties[] = {0x83, 0x00, 0x80, 0x84, 0x80, 0x81, 0x00, 0xa0,
                              0xa3, 0x01, 0xa0, 0x02, 0xa0, 0x03, 0x80};
 
-int32_t EmptyMapsAndArraysTest()
+/* Same as above, but with indefinte lengths */
+static uint8_t sEmptiesIndef[] = {
+0x9F,
+   0x00,
+   0x9F,
+      0xFF,
+   0x9F,
+      0x9F,
+         0xFF,
+      0x9F,
+         0x00,
+         0xFF,
+      0xBF,
+         0xFF,
+      0xBF,
+         0x01,
+         0xBF,
+            0xFF,
+         0x02,
+         0xBF,
+            0xFF,
+         0x03,
+         0x9F,
+            0xFF,
+         0xFF,
+      0xFF,
+   0xFF};
+
+
+
+static int32_t CheckEmpties(UsefulBufC input, bool bCheckCounts)
 {
    QCBORDecodeContext DCtx;
    QCBORItem Item;
 
    QCBORDecode_Init(&DCtx,
-                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmpties),
+                    input,
                     QCBOR_DECODE_MODE_NORMAL);
 
    // Array with 3 items
@@ -657,7 +721,7 @@
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.uNestingLevel != 0 ||
       Item.uNextNestLevel != 1 ||
-      Item.val.uCount != 3) {
+      (bCheckCounts && Item.val.uCount != 3)) {
       return -1;
    }
 
@@ -675,7 +739,7 @@
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.uNestingLevel != 1 ||
       Item.uNextNestLevel != 1 ||
-      Item.val.uCount != 0) {
+      (bCheckCounts && Item.val.uCount != 0)) {
       return -3;
    }
 
@@ -684,7 +748,7 @@
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.uNestingLevel != 1 ||
       Item.uNextNestLevel != 2 ||
-      Item.val.uCount != 4) {
+      (bCheckCounts && Item.val.uCount != 4)) {
       return -4;
    }
 
@@ -693,7 +757,7 @@
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.uNestingLevel != 2 ||
       Item.uNextNestLevel != 2 ||
-      Item.val.uCount != 0) {
+      (bCheckCounts && Item.val.uCount != 0)) {
       return -5;
    }
 
@@ -702,7 +766,7 @@
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.uNestingLevel != 2 ||
       Item.uNextNestLevel != 3 ||
-      Item.val.uCount != 1) {
+      (bCheckCounts && Item.val.uCount != 1)) {
       return -6;
    }
 
@@ -720,16 +784,16 @@
       Item.uDataType != QCBOR_TYPE_MAP ||
       Item.uNestingLevel != 2 ||
       Item.uNextNestLevel != 2 ||
-      Item.val.uCount != 0) {
+      (bCheckCounts && Item.val.uCount != 0)) {
       return -8;
    }
 
-   // An map with 3 items
+   // A map with 3 items
    if(QCBORDecode_GetNext(&DCtx, &Item) != 0 ||
       Item.uDataType != QCBOR_TYPE_MAP ||
       Item.uNestingLevel != 2 ||
       Item.uNextNestLevel != 3 ||
-      Item.val.uCount != 3) {
+      (bCheckCounts && Item.val.uCount != 3)) {
       return -9;
    }
 
@@ -738,7 +802,7 @@
       Item.uDataType != QCBOR_TYPE_MAP ||
       Item.uNestingLevel != 3 ||
       Item.uNextNestLevel != 3 ||
-      Item.val.uCount != 0) {
+      (bCheckCounts && Item.val.uCount != 0)) {
       return -10;
    }
 
@@ -747,7 +811,7 @@
       Item.uDataType != QCBOR_TYPE_MAP ||
       Item.uNestingLevel != 3 ||
       Item.uNextNestLevel != 3 ||
-      Item.val.uCount != 0) {
+      (bCheckCounts && Item.val.uCount != 0)) {
       return -11;
    }
 
@@ -756,13 +820,32 @@
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.uNestingLevel != 3 ||
       Item.uNextNestLevel != 0 ||
-      Item.val.uCount != 0) {
+      (bCheckCounts && Item.val.uCount != 0)) {
       return -12;
    }
 
    if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
       return -13;
    }
+   return 0;
+}
+
+
+int32_t EmptyMapsAndArraysTest()
+{
+   int nResult;
+   nResult = CheckEmpties(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmpties),
+                     true);
+   if(nResult) {
+      return nResult;
+   }
+
+   nResult = CheckEmpties(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmptiesIndef),
+                     false);
+
+   if(nResult) {
+      return nResult -100;
+   }
 
    return 0;
 }
@@ -823,7 +906,7 @@
       }
    }
 
-   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP)
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP)
       nReturn = -1;
 
    return(nReturn);
@@ -873,7 +956,8 @@
    }
 
    for(nEncodedLen--; nEncodedLen; nEncodedLen--) {
-      int nResult = ParseOrderedArray(pEncoded, (uint32_t)nEncodedLen, &i1, &i2, &s3, &i3, &s4, &i4);
+      int nResult = ParseOrderedArray(pEncoded, (uint32_t)nEncodedLen, &i1,
+                                      &i2, &s3, &i3, &s4, &i4);
       if(nResult == 0) {
          nReturn = -1;
       }
@@ -907,12 +991,13 @@
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
       return (int32_t)nCBORError;
    }
+
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataType != QCBOR_TYPE_INT64 ||
       Item.val.int64 != 42 ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("first integer"))) {
+      UsefulBufCompareToSZ(Item.label.string, "first integer")) {
       return -1;
    }
 
@@ -922,7 +1007,7 @@
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("an array of two strings")) ||
+      UsefulBufCompareToSZ(Item.label.string, "an array of two strings") ||
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.val.uCount != 2)
       return -1;
@@ -933,7 +1018,7 @@
    if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string1"))) {
+      UsefulBufCompareToSZ(Item.val.string, "string1")) {
       return -1;
    }
 
@@ -943,7 +1028,7 @@
    if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string2"))) {
+      UsefulBufCompareToSZ(Item.val.string, "string2")) {
       return -1;
    }
 
@@ -953,7 +1038,7 @@
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("map in a map")) ||
+      UsefulBufCompareToSZ(Item.label.string, "map in a map") ||
       Item.uDataType != QCBOR_TYPE_MAP ||
       Item.val.uCount != 4) {
       return -1;
@@ -967,7 +1052,7 @@
       Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("xxxx"))) {
+      UsefulBufCompareToSZ(Item.val.string, "xxxx")) {
       return -1;
    }
 
@@ -975,11 +1060,11 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 2")) ||
+      UsefulBufCompareToSZ(Item.label.string, "bytes 2") ||
       Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("yyyy"))) {
+      UsefulBufCompareToSZ(Item.val.string, "yyyy")) {
       return -1;
    }
 
@@ -989,7 +1074,7 @@
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("another int")) ||
+      UsefulBufCompareToSZ(Item.label.string, "another int") ||
       Item.uDataType != QCBOR_TYPE_INT64 ||
       Item.val.int64 != 98)
       return -1;
@@ -1002,7 +1087,7 @@
       Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
+      UsefulBufCompareToSZ(Item.val.string, "lies, damn lies and statistics")) {
       return -1;
    }
 
@@ -1012,7 +1097,7 @@
 
 /*
  Decode and thoroughly check a moderately complex
- set of maps
+ set of maps in the QCBOR_DECODE_MODE_MAP_AS_ARRAY mode.
  */
 int32_t ParseMapAsArrayTest()
 {
@@ -1039,7 +1124,7 @@
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
       Item.uLabelType != QCBOR_TYPE_NONE ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("first integer"))) {
+      UsefulBufCompareToSZ(Item.val.string, "first integer")) {
       return -2;
    }
 
@@ -1060,7 +1145,7 @@
    if(Item.uLabelType != QCBOR_TYPE_NONE ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("an array of two strings")) ||
+      UsefulBufCompareToSZ(Item.val.string, "an array of two strings") ||
       Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
       return -4;
    }
@@ -1104,7 +1189,7 @@
    if(Item.uLabelType != QCBOR_TYPE_NONE ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("map in a map"))) {
+      UsefulBufCompareToSZ(Item.val.string, "map in a map")) {
       return -8;
    }
 
@@ -1123,7 +1208,7 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_NONE ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("bytes 1"))||
+      UsefulBufCompareToSZ(Item.val.string, "bytes 1") ||
       Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc) {
@@ -1137,7 +1222,7 @@
       Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("xxxx"))) {
+      UsefulBufCompareToSZ(Item.val.string, "xxxx")) {
       return -11;
    }
 
@@ -1145,7 +1230,7 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_NONE ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("bytes 2")) ||
+      UsefulBufCompareToSZ(Item.val.string, "bytes 2") ||
       Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc) {
@@ -1159,7 +1244,7 @@
       Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("yyyy"))) {
+      UsefulBufCompareToSZ(Item.val.string, "yyyy")) {
       return -13;
    }
 
@@ -1169,7 +1254,7 @@
    if(Item.uLabelType != QCBOR_TYPE_NONE ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("another int")) ||
+      UsefulBufCompareToSZ(Item.val.string, "another int") ||
       Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
       return -14;
    }
@@ -1189,7 +1274,7 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_NONE ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("text 2"))||
+      UsefulBufCompareToSZ(Item.val.string, "text 2") ||
       Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc) {
@@ -1203,7 +1288,7 @@
       Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataAlloc ||
       Item.uLabelAlloc ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
+      UsefulBufCompareToSZ(Item.val.string, "lies, damn lies and statistics")) {
       return -17;
    }
 
@@ -1219,10 +1304,13 @@
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pTooLargeMap),
                     QCBOR_DECODE_MODE_MAP_AS_ARRAY);
 
-   if((QCBOR_ERR_ARRAY_TOO_LONG != QCBORDecode_GetNext(&DCtx, &Item))) {
+   if((QCBOR_ERR_ARRAY_DECODE_TOO_LONG != QCBORDecode_GetNext(&DCtx, &Item))) {
       return -50;
    }
 
+   // TODO: test decoding of labels that are arrays or such
+   // TODO: test spiffy decoding of QCBOR_DECODE_MODE_MAP_AS_ARRAY
+
    return 0;
 }
 
@@ -1263,11 +1351,11 @@
    }
    if(Item.uDataType != QCBOR_TYPE_MAP ||
       Item.val.uCount != 3)
-      return -1;
+      return -2;
 
    if(nLevel < 2) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -3;
       } else {
          return 0;
       }
@@ -1280,13 +1368,13 @@
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       Item.uDataType != QCBOR_TYPE_INT64 ||
       Item.val.uCount != 42 ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("first integer"))) {
-      return -1;
+      UsefulBufCompareToSZ(Item.label.string, "first integer")) {
+      return -4;
    }
 
    if(nLevel < 3) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -5;
       } else {
          return 0;
       }
@@ -1296,16 +1384,16 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("an array of two strings")) ||
+      UsefulBufCompareToSZ(Item.label.string, "an array of two strings") ||
       Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.val.uCount != 2) {
-      return -1;
+      return -6;
    }
 
 
    if(nLevel < 4) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -7;
       } else {
          return 0;
       }
@@ -1316,13 +1404,13 @@
       return (int32_t)nCBORError;
    }
    if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string1"))) {
-      return -1;
+      UsefulBufCompareToSZ(Item.val.string, "string1")) {
+      return -8;
    }
 
    if(nLevel < 5) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -9;
       } else {
          return 0;
       }
@@ -1332,13 +1420,13 @@
       return (int32_t)nCBORError;
    }
    if(Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("string2"))) {
-      return -1;
+      UsefulBufCompareToSZ(Item.val.string, "string2")) {
+      return -10;
    }
 
    if(nLevel < 6) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -11;
       } else {
          return 0;
       }
@@ -1348,14 +1436,14 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("map in a map")) ||
+      UsefulBufCompareToSZ(Item.label.string, "map in a map") ||
       Item.uDataType != QCBOR_TYPE_MAP ||
       Item.val.uCount != 4)
-      return -1;
+      return -12;
 
    if(nLevel < 7) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -13;
       } else {
          return 0;
       }
@@ -1365,15 +1453,15 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 1")) ||
+      UsefulBufCompareToSZ(Item.label.string, "bytes 1") ||
       Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("xxxx"))) {
-      return -1;
+      UsefulBufCompareToSZ(Item.val.string, "xxxx")) {
+      return -14;
    }
 
    if(nLevel < 8) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -15;
       } else {
          return 0;
       }
@@ -1383,15 +1471,15 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("bytes 2")) ||
+      UsefulBufCompareToSZ(Item.label.string, "bytes 2") ||
       Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("yyyy"))) {
-      return -1;
+      UsefulBufCompareToSZ(Item.val.string, "yyyy")) {
+      return -16;
    }
 
    if(nLevel < 9) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -17;
       } else {
          return 0;
       }
@@ -1401,14 +1489,14 @@
       return (int32_t)nCBORError;
    }
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("another int")) ||
+      UsefulBufCompareToSZ(Item.label.string, "another int") ||
       Item.uDataType != QCBOR_TYPE_INT64 ||
       Item.val.int64 != 98)
-      return -1;
+      return -18;
 
    if(nLevel < 10) {
-      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
-         return -1;
+      if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
+         return -19;
       } else {
          return 0;
       }
@@ -1420,12 +1508,12 @@
    if(Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       UsefulBuf_Compare(Item.label.string, UsefulBuf_FromSZ("text 2"))||
       Item.uDataType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
-      return -1;
+      UsefulBufCompareToSZ(Item.val.string, "lies, damn lies and statistics")) {
+      return -20;
    }
 
    if(QCBORDecode_Finish(&DCtx)) {
-      return -1;
+      return -21;
    }
 
    return 0;
@@ -1543,24 +1631,6 @@
 }
 
 
-static int IsNotWellFormedError(QCBORError nErr)
-{
-   switch(nErr){
-      case QCBOR_ERR_INDEFINITE_STRING_CHUNK:
-      case QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN:
-      case QCBOR_ERR_UNSUPPORTED:
-      case QCBOR_ERR_HIT_END:
-      case QCBOR_ERR_BAD_TYPE_7:
-      case QCBOR_ERR_BAD_BREAK:
-      case QCBOR_ERR_EXTRA_BYTES:
-      case QCBOR_ERR_BAD_INT:
-         return 1;
-      default:
-         return 0;
-   }
-}
-
-
 int32_t NotWellFormedTests()
 {
    // Loop over all the not-well-formed instance of CBOR
@@ -1578,17 +1648,18 @@
       QCBORDecode_SetMemPool(&DCtx, Pool, 0);
 
       // Loop getting items until no more to get
-      QCBORError nCBORError;
+      QCBORError uCBORError;
       do {
          QCBORItem Item;
 
-         nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
-      } while(nCBORError == QCBOR_SUCCESS);
+         uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+      } while(uCBORError == QCBOR_SUCCESS);
 
       // Every test vector must fail with
       // a not-well-formed error. If not
       // this test fails.
-      if(!IsNotWellFormedError(nCBORError)) {
+      if(!QCBORDecode_IsNotWellFormedError(uCBORError) &&
+         uCBORError != QCBOR_ERR_NO_MORE_ITEMS) {
          // Return index of failure in the error code
          return 2000 + nIterate;
       }
@@ -1611,6 +1682,7 @@
       QCBORDecodeContext DCtx;
       QCBORDecode_Init(&DCtx, pF->Input, QCBOR_DECODE_MODE_NORMAL);
       UsefulBuf_MAKE_STACK_UB(Pool, 100);
+
       QCBORError nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
       if(nCBORError) {
          return -9;
@@ -1619,7 +1691,7 @@
       // Iterate until there is an error of some sort error
       QCBORItem Item;
       do {
-         // Set to something none-zero other than QCBOR_TYPE_NONE
+         // Set to something none-zero, something other than QCBOR_TYPE_NONE
          memset(&Item, 0x33, sizeof(Item));
 
          nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
@@ -1631,7 +1703,7 @@
          Item.uDataType != QCBOR_TYPE_NONE ||
          Item.uLabelType != QCBOR_TYPE_NONE) {
          // return index of CBOR + 100
-         const size_t nIndex = (size_t)(pF - pFailInputs)/sizeof(struct FailInput);
+         const size_t nIndex = (size_t)(pF - pFailInputs);
          return (int32_t)(nIndex * 100 + nCBORError);
       }
    }
@@ -1651,7 +1723,8 @@
    { {(uint8_t[]){0x7f, 0x61, 0x00}, 3}, QCBOR_ERR_HIT_END },
 
 
-   // All the chunks in an indefinite length string must be of the type of indefinite length string
+   // All the chunks in an indefinite length string must be of the type of
+   // indefinite length string
    // indefinite length byte string with text string chunk
    { {(uint8_t[]){0x5f, 0x61, 0x00, 0xff}, 4}, QCBOR_ERR_INDEFINITE_STRING_CHUNK },
    // indefinite length text string with a byte string chunk
@@ -1675,43 +1748,59 @@
 
    // Definte length maps and arrays must be closed by having the right number of items
    // A definte length array that is supposed to have 1 item, but has none
-   { {(uint8_t[]){0x81}, 1}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0x81}, 1}, QCBOR_ERR_NO_MORE_ITEMS },
    // A definte length array that is supposed to have 2 items, but has only 1
-   { {(uint8_t[]){0x82, 0x00}, 2}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0x82, 0x00}, 2}, QCBOR_ERR_NO_MORE_ITEMS },
    // A definte length array that is supposed to have 511 items, but has only 1
    { {(uint8_t[]){0x9a, 0x01, 0xff, 0x00}, 4}, QCBOR_ERR_HIT_END },
    // A definte length map that is supposed to have 1 item, but has none
-   { {(uint8_t[]){0xa1}, 1}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0xa1}, 1}, QCBOR_ERR_NO_MORE_ITEMS },
    // A definte length map that is supposed to have s item, but has only 1
-   { {(uint8_t[]){0xa2, 0x01, 0x02}, 3}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0xa2, 0x01, 0x02}, 3}, QCBOR_ERR_NO_MORE_ITEMS },
 
 
    // Indefinte length maps and arrays must be ended by a break
    // Indefinite length array with zero items and no break
-   { {(uint8_t[]){0x9f}, 1}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0x9f}, 1}, QCBOR_ERR_NO_MORE_ITEMS },
    // Indefinite length array with two items and no break
-   { {(uint8_t[]){0x9f, 0x01, 0x02}, 3}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0x9f, 0x01, 0x02}, 3}, QCBOR_ERR_NO_MORE_ITEMS },
    // Indefinite length map with zero items and no break
-   { {(uint8_t[]){0xbf}, 1}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0xbf}, 1}, QCBOR_ERR_NO_MORE_ITEMS },
    // Indefinite length map with two items and no break
-   { {(uint8_t[]){0xbf, 0x01, 0x02, 0x01, 0x02}, 5}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0xbf, 0x01, 0x02, 0x01, 0x02}, 5}, QCBOR_ERR_NO_MORE_ITEMS },
 
 
    // Nested maps and arrays must be closed off (some extra nested test vectors)
-   // Unclosed indefinite array containing a close definite array
-   { {(uint8_t[]){0x9f, 0x80, 0x00}, 3}, QCBOR_ERR_HIT_END },
-   // Definite length array containing an unclosed indefinite array
-   { {(uint8_t[]){0x81, 0x9f}, 2}, QCBOR_ERR_HIT_END },
+   // Unclosed indefinite array containing a closed definite length array
+   { {(uint8_t[]){0x9f, 0x80, 0x00}, 3}, QCBOR_ERR_NO_MORE_ITEMS },
+   // Definite length array containing an unclosed indefinite length array
+   { {(uint8_t[]){0x81, 0x9f}, 2}, QCBOR_ERR_NO_MORE_ITEMS },
+   // Unclosed indefinite map containing a closed definite length array
+   { {(uint8_t[]){0xbf, 0x01, 0x80, 0x00, 0xa0}, 5}, QCBOR_ERR_NO_MORE_ITEMS },
+   // Definite length map containing an unclosed indefinite length array
+   { {(uint8_t[]){0xa1, 0x02, 0x9f}, 3}, QCBOR_ERR_NO_MORE_ITEMS },
    // Deeply nested definite length arrays with deepest one unclosed
-   { {(uint8_t[]){0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81}, 9}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81}, 9}, QCBOR_ERR_NO_MORE_ITEMS },
    // Deeply nested indefinite length arrays with deepest one unclosed
-   { {(uint8_t[]){0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xff, 0xff, 0xff, 0xff}, 9}, QCBOR_ERR_HIT_END },
+   { {(uint8_t[]){0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xff, 0xff, 0xff, 0xff}, 9}, QCBOR_ERR_NO_MORE_ITEMS },
    // Mixed nesting with indefinite unclosed
-   // TODO: think through this one
-   { {(uint8_t[]){0x9f, 0x81, 0x9f, 0x81, 0x9f, 0x9f, 0xff, 0xff, 0xff}, 9}, QCBOR_ERR_BAD_BREAK },
+   { {(uint8_t[]){0x9f, 0x81, 0x9f, 0x81, 0x9f, 0x9f, 0xff, 0xff, 0xff}, 9}, QCBOR_ERR_NO_MORE_ITEMS },
    // Mixed nesting with definite unclosed
-   // TODO: think through this one
    { {(uint8_t[]){0x9f, 0x82, 0x9f, 0x81, 0x9f, 0x9f, 0xff, 0xff, 0xff, 0xff}, 10}, QCBOR_ERR_BAD_BREAK },
+   // Unclosed indefinite length map in definite length maps
+   { {(uint8_t[]){0xa1, 0x01, 0xa2, 0x02, 0xbf, 0xff, 0x02, 0xbf}, 8},
+      QCBOR_ERR_NO_MORE_ITEMS},
+   // Unclosed definite length map in indefinite length maps
+   { {(uint8_t[]){0xbf, 0x01, 0xbf, 0x02, 0xa1}, 5}, QCBOR_ERR_NO_MORE_ITEMS},
+   // Unclosed indefinite length array in definite length maps
+   { {(uint8_t[]){0xa1, 0x01, 0xa2, 0x02, 0x9f, 0xff, 0x02, 0x9f}, 8},
+      QCBOR_ERR_NO_MORE_ITEMS},
+   // Unclosed definite length array in indefinite length maps
+   { {(uint8_t[]){0xbf, 0x01, 0xbf, 0x02, 0x81}, 5}, QCBOR_ERR_NO_MORE_ITEMS},
+   // Unclosed indefinite length map in definite length arrays
+   { {(uint8_t[]){0x81, 0x82, 0xbf, 0xff, 0xbf}, 5}, QCBOR_ERR_NO_MORE_ITEMS},
+   // Unclosed definite length map in indefinite length arrays
+   { {(uint8_t[]){0x9f, 0x9f, 0xa1}, 3}, QCBOR_ERR_NO_MORE_ITEMS},
 
 
    // The "argument" for the data item is incomplete
@@ -1743,6 +1832,16 @@
    { {(uint8_t[]){0xd8}, 1}, QCBOR_ERR_HIT_END },
    // Simple missing 1 byte argument
    { {(uint8_t[]){0xf8}, 1}, QCBOR_ERR_HIT_END },
+   // half-precision with 1 byte argument
+   { {(uint8_t[]){0xf9, 0x00}, 2}, QCBOR_ERR_HIT_END },
+   // single-precision with 2 byte argument
+   { {(uint8_t[]){0xfa, 0x00, 0x00}, 3}, QCBOR_ERR_HIT_END },
+   // double-precision with 3 byte argument
+   { {(uint8_t[]){0xfb, 0x00, 0x00, 0x00}, 4}, QCBOR_ERR_HIT_END },
+
+
+   // Tag with no content
+   { {(uint8_t[]){0xc0}, 1}, QCBOR_ERR_HIT_END },
 
 
    // Breaks must not occur in definite length arrays and maps
@@ -1753,7 +1852,7 @@
    // Map of length 1 with sole member label replaced by a break
    { {(uint8_t[]){0xa1, 0xff}, 2}, QCBOR_ERR_BAD_BREAK },
    // Map of length 1 with sole member label replaced by break
-   // Alternate representation that some decoders handle difference
+   // Alternate representation that some decoders handle differently
    { {(uint8_t[]){0xa1, 0xff, 0x00}, 3}, QCBOR_ERR_BAD_BREAK },
    // Array of length 1 with 2nd member value replaced by a break
    { {(uint8_t[]){0xa1, 0x00, 0xff}, 3}, QCBOR_ERR_BAD_BREAK },
@@ -1768,6 +1867,10 @@
    { {(uint8_t[]){0x80, 0xff}, 2}, QCBOR_ERR_BAD_BREAK },
    // A bare break after a zero length indefinite length map
    { {(uint8_t[]){0x9f, 0xff, 0xff}, 3}, QCBOR_ERR_BAD_BREAK },
+   // A break inside a definite length array inside an indefenite length array
+   { {(uint8_t[]){0x9f, 0x81, 0xff}, 3}, QCBOR_ERR_BAD_BREAK },
+   // Complicated mixed nesting with break outside indefinite length array
+   { {(uint8_t[]){0x9f, 0x82, 0x9f, 0x81, 0x9f, 0x9f, 0xff, 0xff, 0xff, 0xff}, 10}, QCBOR_ERR_BAD_BREAK },
 
 
    // Forbidden two byte encodings of simple types
@@ -1821,7 +1924,8 @@
    { {(uint8_t[]){0xf8, 0x17}, 2}, QCBOR_ERR_BAD_TYPE_7 },
    // Must use 0xf8 instead
    { {(uint8_t[]){0xf8, 0x18}, 2}, QCBOR_ERR_BAD_TYPE_7 },
-
+   // Reserved
+   { {(uint8_t[]){0xf8, 0x1f}, 2}, QCBOR_ERR_BAD_TYPE_7 },
 
    // Integers with additional info indefinite length
    // Positive integer with additional info indefinite length
@@ -1843,7 +1947,12 @@
    { {(uint8_t[]){0x5a, 0xff, 0xff, 0xff, 0xf0, 0x00}, 6}, QCBOR_ERR_HIT_END },
    // Byte string should have 2^32-15 bytes, but has one
    { {(uint8_t[]){0x7a, 0xff, 0xff, 0xff, 0xf0, 0x00}, 6}, QCBOR_ERR_HIT_END },
-
+   // Byte string should have 2^64 bytes, but has 3
+   { {(uint8_t[]){0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                  0x01, 0x02, 0x03}, 6}, QCBOR_ERR_HIT_END },
+   // Text string should have 2^64 bytes, but has 3
+   { {(uint8_t[]){0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                  0x01, 0x02, 0x03}, 6}, QCBOR_ERR_HIT_END },
 
    // Use of unassigned additional information values
    // Major type positive integer with reserved value 28
@@ -1929,54 +2038,54 @@
 
    // Corrupt the UsefulInputBuf and see that
    // it reflected correctly for CBOR decoding
-   {
-      QCBORDecodeContext DCtx;
-      QCBORItem          Item;
-      QCBORError         nCBORError;
+   QCBORDecodeContext DCtx;
+   QCBORItem          Item;
+   QCBORError         uQCBORError;
 
-      QCBORDecode_Init(&DCtx,
-                       UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSimpleValues),
-                       QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSimpleValues),
+                    QCBOR_DECODE_MODE_NORMAL);
 
-      if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-         return (int32_t)nCBORError;
-      if(Item.uDataType != QCBOR_TYPE_ARRAY ||
-         Item.val.uCount != 10) {
-         // This wasn't supposed to happen
-         return -1;
-      }
-
-      DCtx.InBuf.magic = 0; // Reach in and corrupt the UsefulInputBuf
-
-      nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
-      if(nCBORError != QCBOR_ERR_HIT_END) {
-         // Did not get back the error expected
-         return -2;
-      }
+   if((uQCBORError = QCBORDecode_GetNext(&DCtx, &Item))) {
+      return (int32_t)uQCBORError;
+   }
+   if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.val.uCount != 10) {
+      // This wasn't supposed to happen
+      return -1;
    }
 
-/*
-   This test is disabled until QCBOREncode_EncodeHead() is brought in so
- the size encoded can be tied to SIZE_MAX and work for all size CPUs.
+   DCtx.InBuf.magic = 0; // Reach in and corrupt the UsefulInputBuf
 
- This relies on the largest string allowed being SIZE_MAX -4 rather than
- SIZE_MAX. That way the test can be performed.
-   {
-      QCBORDecodeContext DCtx;
-      QCBORItem          Item;
-
-      static uint8_t foo[] = {0x5b, 0xff, 0xff, 0xff, 0xff,
-                                    0xff, 0xff, 0xff, 0xff};
-
-      QCBORDecode_Init(&DCtx,
-                       UsefulBuf_FROM_BYTE_ARRAY_LITERAL(foo),
-                       QCBOR_DECODE_MODE_NORMAL);
-
-      if(QCBOR_ERR_STRING_TOO_LONG != QCBORDecode_GetNext(&DCtx, &Item)) {
-         return -4;
-      }
+   uQCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uQCBORError != QCBOR_ERR_NO_MORE_ITEMS) {
+      // Did not get back the error expected
+      return -2;
    }
-*/
+
+
+   /*
+    The max size of a string for QCBOR is SIZE_MAX - 4 so this
+    tests here can be performed to see that the max length
+    error check works correctly. See DecodeBytes(). If the max
+    size was SIZE_MAX, it wouldn't be possible to test this.
+
+    This test will automatocally adapt the all CPU sizes
+    through the use of SIZE_MAX.
+   */
+
+   MakeUsefulBufOnStack(  HeadBuf, QCBOR_HEAD_BUFFER_SIZE);
+   UsefulBufC             EncodedHead;
+
+   // This makes a CBOR head with a text string that is very long
+   // but doesn't fill in the bytes of the text string as that is
+   // not needed to test this part of QCBOR.
+   EncodedHead = QCBOREncode_EncodeHead(HeadBuf, CBOR_MAJOR_TYPE_TEXT_STRING, 0, SIZE_MAX);
+
+   QCBORDecode_Init(&DCtx, EncodedHead, QCBOR_DECODE_MODE_NORMAL);
+
+   if(QCBOR_ERR_STRING_TOO_LONG != QCBORDecode_GetNext(&DCtx, &Item)) {
+      return -4;
+   }
 
    return 0;
 }
@@ -2049,11 +2158,17 @@
    0xc0, // tag for string date
    0x6a, '1','9','8','5','-','0','4','-','1','2', // Date string
 
+   0xc0, // tag for string date
+   0x00, // Wrong type for a string date
+
    0xc1, // tag for epoch date
    0x1a, 0x53, 0x72, 0x4E, 0x00, // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
 
+   0xc1,
+   0x62, 'h', 'i', // wrong type tagged
+
    // CBOR_TAG_B64
-   0xc1, 0xcf, 0xd8, 0x22, // 0xee, // Epoch date with extra tags
+   0xcf, 0xd8, 0x22, 0xc1, // 0xee, // Epoch date with extra tags
    0x1a, 0x53, 0x72, 0x4E, 0x01,
 
    0xc1, // tag for epoch date
@@ -2070,11 +2185,22 @@
    //0xfa, 0x7f, 0x7f, 0xff, 0xff // 3.4028234663852886e+38 too large
 
    0xc1, // tag for epoch date
-   0xfb, 0x43, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe // 9223372036854773760 largest supported
+   0xfb, 0x43, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, // 9223372036854773760 largest supported
+
+   0xc1, // tag for epoch date
+   0xfa, 0x7f, 0xc0, 0x00, 0x00, // Single-precision NaN
+
+   0xc1,
+   0xfb, 0x7f,  0xf0,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // +infinity
+
+   0xc1, // tag for epoch date
+   0xf9, 0xfc, 0x00, // -Infinity
 };
 
 
+
 // have to check float expected only to within an epsilon
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
 static int CHECK_EXPECTED_DOUBLE(double val, double expected) {
 
    double diff = val - expected;
@@ -2083,74 +2209,85 @@
 
    return diff > 0.0000001;
 }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
 
 
 int32_t DateParseTest()
 {
    QCBORDecodeContext DCtx;
-   QCBORItem Item;
-   QCBORError nCBORError;
+   QCBORItem          Item;
+   QCBORError         uError;
 
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spDateTestInput),
                     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)))
+   if((uError = QCBORDecode_GetNext(&DCtx, &Item))) {
       return -1;
+   }
    if(Item.uDataType != QCBOR_TYPE_DATE_STRING ||
-      UsefulBuf_Compare(Item.val.dateString, UsefulBuf_FromSZ("1985-04-12"))){
+      UsefulBufCompareToSZ(Item.val.dateString, "1985-04-12")){
       return -2;
    }
 
-   // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
+   // Wrong type for a string date
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError != QCBOR_ERR_BAD_OPT_TAG) {
       return -3;
+   }
+
+   // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
+   if((uError = QCBORDecode_GetNext(&DCtx, &Item))) {
+      return -4;
+   }
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1400000000 ||
       Item.val.epochDate.fSecondsFraction != 0 ) {
-      return -4;
+      return -5;
+   }
+
+   // Wrong type for an epoch date
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_BAD_OPT_TAG) {
+      return -6;
    }
 
    // 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 -5;
+   if((uError = QCBORDecode_GetNext(&DCtx, &Item))) {
+      return -7;
+   }
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1400000001 ||
       Item.val.epochDate.fSecondsFraction != 0 ||
       !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_B64)) {
-      return -6;
+      return -8;
    }
 
    // Epoch date that is too large for our representation
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
-      return -7;
+      return -9;
    }
 
-   // Epoch date in float format with fractional seconds
 #ifndef QCBOR_DISABLE_FLOAT_HW_USE
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -8;
+   // Epoch date in float format with fractional seconds
+   if((uError = QCBORDecode_GetNext(&DCtx, &Item))) {
+      return -10;
+   }
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1 ||
       CHECK_EXPECTED_DOUBLE(Item.val.epochDate.fSecondsFraction, 0.1 )) {
-      return -9;
+      return -11;
    }
 
    // Epoch date float that is too large for our representation
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
-      return -10;
+      return -12;
    }
 
    // Epoch date double that is just slightly too large
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
-      return -11;
+      return -13;
    }
 
    // Largest double epoch date supported
@@ -2158,40 +2295,442 @@
        Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
        Item.val.epochDate.nSeconds != 9223372036854773760 ||
        Item.val.epochDate.nSeconds == 0) {
-       return -12;
+       return -14;
     }
+
+   // Nan
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
+      return -15;
+   }
+
+   // +Inifinity double-precision
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
+      return -16;
+   }
+
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+   // -Inifinity half-precision
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
+      return -17;
+   }
 #else
-   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
-      return -80;
-   }
-   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
-      return -80;
-   }
-   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
-      return -80;
-   }
-   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
-      return -80;
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_HALF_PRECISION_DISABLED) {
+      return -18;
    }
 #endif
 
+#else
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -19;
+   }
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -20;
+   }
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -21;
+   }
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -22;
+   }
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -23;
+   }
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -24;
+   }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return -25;
+   }
+#else
+   if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_HALF_PRECISION_DISABLED) {
+      return -26;
+   }
+#endif
 
-   // TODO: could use a few more tests with float, double, and half precsion
-   // and negative (but coverage is still pretty good)
+#endif
 
    return 0;
 }
 
-// Really simple basic input for tagging test
-static uint8_t spOptTestInput[] = {
+/*
+ Test cases covered here. Some items cover more than one of these.
+   positive integer (zero counts as a positive integer)
+   negative integer
+   half-precision float
+   single-precision float
+   double-precision float
+
+   float Overflow error
+   Wrong type error for epoch
+   Wrong type error for date string
+   float disabled error
+   half-precision disabled error
+   -Infinity
+   Slightly too large integer
+   Slightly too far from zero
+
+   Get epoch by int
+   Get string by int
+   Get epoch by string
+   Get string by string
+   Fail to get epoch by wrong int label
+   Fail to get string by wrong string label
+   Fail to get epoch by string because it is invalid
+   Fail to get epoch by int because it is invalid
+
+   Untagged values
+ */
+static uint8_t spSpiffyDateTestInput[] = {
+   0x86,
+
+   0xc1,
+   0xfb, 0xc3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // -9.2233720368547748E+18, too negative
+
+   0xc1, // tag for epoch date
+   0x1b, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // Too-large integer
+
+   0xc1, // tag for epoch date
+   0xf9, 0xfc, 0x00, // Half-precision -Infinity
+
+   0xc1, // tag for epoch date
+   0x9f, 0xff, // Erroneous empty array as content for date
+
+   0xc0, // tag for string date
+   0xbf, 0xff, // Erroneous empty map as content for date
+
+   0xbf, // Open a map for tests involving labels.
+
+   0x00,
+   0xc0, // tag for string date
+   0x6a, '1','9','8','5','-','0','4','-','1','2', // Tagged date string
+
+   0x01,
+   0xda, 0x03, 0x03, 0x03, 0x03, // An additional tag
+   0xc1, // tag for epoch date
+   0x1a, 0x53, 0x72, 0x4E, 0x00, // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
+
+   // Untagged integer 0
+   0x08,
+   0x00,
+
+   // Utagged date string with string label y
+   0x61, 0x79,
+   0x6a, '2','0','8','5','-','0','4','-','1','2', // Untagged date string
+
+   // Untagged -1000 with label z
+   0x61, 0x7a,
+   0xda, 0x01, 0x01, 0x01, 0x01, // An additional tag
+   0x39, 0x03, 0xe7,
+
+   0x07,
+   0xc1, // tag for epoch date
+   0xfb, 0x43, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, // 9223372036854773760 largest supported
+
+   0x05,
+   0xc1,
+   0xfb, 0xc3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, // -9223372036854773760 largest negative
+
+   // Untagged single-precision float with value 3.14 with string label x
+   0x61, 0x78,
+   0xFA, 0x40, 0x48, 0xF5, 0xC3,
+
+   // Untagged half-precision float with value -2
+   0x09,
+   0xF9, 0xC0, 0x00,
+
+   0xff,
+};
+
+int32_t SpiffyDateDecodeTest()
+{
+   QCBORDecodeContext DC;
+   QCBORError         uError;
+   int64_t            nEpochDate2, nEpochDate3, nEpochDate5,
+                      nEpochDate4, nEpochDate6, nEpochDateFail,
+                      nEpochDate1400000000;
+   UsefulBufC         StringDate1, StringDate2;
+   uint64_t           uTag1, uTag2;
+
+   QCBORDecode_Init(&DC,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSpiffyDateTestInput),
+                    QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_EnterArray(&DC, NULL);
+
+   // Too-negative float, -9.2233720368547748E+18
+   QCBORDecode_GetEpochDate(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nEpochDateFail);
+   uError = QCBORDecode_GetAndResetError(&DC);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   if(uError != QCBOR_ERR_DATE_OVERFLOW) {
+      return 1111;
+   }
+#else
+   if(uError != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return 1112;
+   }
+#endif
+
+   // Too-large integer
+   QCBORDecode_GetEpochDate(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nEpochDateFail);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_DATE_OVERFLOW) {
+      return 1;
+   }
+
+   // Half-precision minus infinity
+   QCBORDecode_GetEpochDate(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nEpochDateFail);
+   uError = QCBORDecode_GetAndResetError(&DC);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   const QCBORError uExpectedforHalfMinusInfinity = QCBOR_ERR_DATE_OVERFLOW;
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+   const QCBORError uExpectedforHalfMinusInfinity = QCBOR_ERR_FLOAT_DATE_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
+   const QCBORError uExpectedforHalfMinusInfinity = QCBOR_ERR_HALF_PRECISION_DISABLED;
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+   if(uError != uExpectedforHalfMinusInfinity) {
+      return 2;
+   }
+
+   // Bad content for epoch date
+   QCBORDecode_GetEpochDate(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nEpochDateFail);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_BAD_OPT_TAG) {
+      return 3;
+   }
+
+   // Bad content for string date
+   QCBORDecode_GetDateString(&DC, QCBOR_TAG_REQUIREMENT_TAG, &StringDate1);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_BAD_OPT_TAG) {
+      return 4;
+   }
+
+   QCBORDecode_EnterMap(&DC, NULL);
+
+   // Get largest negative double precision epoch date allowed
+   QCBORDecode_GetEpochDateInMapN(&DC,
+                                  5,
+                                  QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG |
+                                    QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS,
+                                  &nEpochDate2);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   if(nEpochDate2 != -9223372036854773760LL) {
+      return 101;
+   }
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return 102;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+   // Get largest double precision epoch date allowed
+   QCBORDecode_GetEpochDateInMapN(&DC, 7, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG,
+                                  &nEpochDate2);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   if(nEpochDate2 != 9223372036854773760ULL) {
+      return 111;
+   }
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return 112;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+   // A single-precision date
+   QCBORDecode_GetEpochDateInMapSZ(&DC, "x", QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG,
+                                   &nEpochDate5);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   if(nEpochDate5 != 3) {
+      return 103;
+   }
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return 104;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+   // A half-precision date with value -2 FFF
+   QCBORDecode_GetEpochDateInMapN(&DC, 9, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG,
+                                  &nEpochDate4);
+#if !defined(QCBOR_DISABLE_FLOAT_HW_USE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+   if(nEpochDate4 != -2) {
+      return 105;
+   }
+#else
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError == QCBOR_SUCCESS) {
+      return 106;
+   }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
+
+   // Fail to get an epoch date by string label
+   QCBORDecode_GetEpochDateInMapSZ(&DC, "no-label",
+                                   QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                   &nEpochDate6);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 107;
+   }
+
+   // Fail to get an epoch date by integer label
+   QCBORDecode_GetEpochDateInMapN(&DC, 99999, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                  &nEpochDate6);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 108;
+   }
+
+   // Fail to get a string date by string label
+   QCBORDecode_GetDateStringInMapSZ(&DC, "no-label",
+                                    QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                    &StringDate1);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 109;
+   }
+
+   // Fail to get a string date by integer label
+   QCBORDecode_GetDateStringInMapN(&DC, 99999, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                   &StringDate1);
+   uError = QCBORDecode_GetAndResetError(&DC);
+   if(uError != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 110;
+   }
+
+   // The rest of these succeed even if float features are disabled
+
+   // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
+   QCBORDecode_GetEpochDateInMapN(&DC,
+                                  1,
+                                  QCBOR_TAG_REQUIREMENT_TAG |
+                                    QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS,
+                                  &nEpochDate1400000000);
+   uTag1 = QCBORDecode_GetNthTagOfLast(&DC, 0);
+   // Tagged date string
+   QCBORDecode_GetDateStringInMapN(&DC, 0, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG,
+                                   &StringDate1);
+   // Untagged integer 0
+   QCBORDecode_GetEpochDateInMapN(&DC, 8, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                  &nEpochDate3);
+   // Untagged date string
+   QCBORDecode_GetDateStringInMapSZ(&DC, "y", QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                    &StringDate2);
+   // Untagged -1000 with label z
+   QCBORDecode_GetEpochDateInMapSZ(&DC,
+                                   "z",
+                                   QCBOR_TAG_REQUIREMENT_NOT_A_TAG |
+                                    QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS,
+                                   &nEpochDate6);
+   uTag2 = QCBORDecode_GetNthTagOfLast(&DC, 0);
+
+   QCBORDecode_ExitMap(&DC);
+   QCBORDecode_ExitArray(&DC);
+   uError = QCBORDecode_Finish(&DC);
+   if(uError) {
+      return 1000 + (int32_t)uError;
+   }
+
+   if(nEpochDate1400000000 != 1400000000) {
+      return 200;
+   }
+
+   if(uTag1 != 0x03030303) {
+      return 201;
+   }
+
+   if(nEpochDate3 != 0) {
+      return 202;
+   }
+
+   if(nEpochDate6 != -1000) {
+      return 203;
+   }
+
+   if(uTag2 != 0x01010101) {
+      return 204;
+   }
+
+   if(UsefulBuf_Compare(StringDate1, UsefulBuf_FromSZ("1985-04-12"))) {
+      return 205;
+   }
+
+   if(UsefulBuf_Compare(StringDate2, UsefulBuf_FromSZ("2085-04-12"))) {
+      return 206;
+   }
+
+   return 0;
+}
+
+
+
+// Input for one of the tagging tests
+static uint8_t spTagInput[] = {
    0xd9, 0xd9, 0xf7, // CBOR magic number
-   0x81, // Array of one
-   0xd8, 0x04, // non-preferred serialization of tag 4
-   0x82, 0x01, 0x03}; // fraction 1/3
+       0x81, // Array of one
+          0xd8, 0x04, // non-preferred serialization of tag 4, decimal fraction
+              0x82, // Array of two that is the faction 1/3
+                 0x01,
+                 0x03,
+
+   /*
+    More than 4 tags on an item 225(226(227(228(229([])))))
+    */
+   0xd8, 0xe1,
+      0xd8, 0xe2,
+          0xd8, 0xe3,
+              0xd8, 0xe4,
+                 0xd8, 0xe5,
+                    0x80,
+
+   /* tag 10489608748473423768(
+             2442302356(
+                21590(
+                   240(
+                      []))))
+    */
+   0xdb, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+      0xda, 0x91, 0x92, 0x93, 0x94,
+         0xd9, 0x54, 0x56,
+            0xd8, 0xf0,
+               0x80,
+
+   /* tag 21590(
+             10489608748473423768(
+                2442302357(
+                   65534(
+                       []))))
+    */
+   0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x56,
+      0xdb, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+         0xda, 0x91, 0x92, 0x93, 0x95,
+            0xd9, 0xff, 0xfe,
+               0x80,
+
+   /* Make sure to blow past the limit of tags that must be mapped.
+      works in conjuntion with entries above.
+    269488144(269488145(269488146(269488147([]))))
+    */
+   0xda, 0x10, 0x10, 0x10, 0x10,
+      0xda, 0x10, 0x10, 0x10, 0x11,
+         0xda, 0x10, 0x10, 0x10, 0x12,
+            0xda, 0x10, 0x10, 0x10, 0x13,
+               0x80,
+
+   /* An invalid decimal fraction with an additional tag */
+   0xd9, 0xff, 0xfa,
+      0xd8, 0x02, // non-preferred serialization of tag 2, a big num
+         0x00, // the integer 0; should be a byte string
+};
 
 /*
  DB 9192939495969798 # tag(10489608748473423768)
-   80               # array(0)
+   80                # array(0)
  */
 static uint8_t spEncodedLargeTag[] = {0xdb, 0x91, 0x92, 0x93, 0x94, 0x95,
                                       0x96, 0x97, 0x98, 0x80};
@@ -2207,12 +2746,26 @@
                                  0x97, 0x98, 0xd8, 0x88, 0xc6, 0xc7, 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: 16(17(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)})})))
+   55799(55799(55799({
+      6(7(-23)): 5859837686836516696(7({
+          7(-20): 11({
+             17(-18): 17(17(17("Organization"))),
+              9(-17): 773("SSG"),
+                 -15: 16(17(6(7("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,
@@ -2227,8 +2780,9 @@
             0xd9, 0x03, 0x05, 0x63,
                0x53, 0x53, 0x47,
             0x2e,
-            0xd0, 0xd1, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0x69,
-               0x43, 0x6f, 0x6e, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e,
+            0xd0, 0xd1, 0xc6, 0xc7,
+               0x69,
+                  0x43, 0x6f, 0x6e, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e,
             0xd1, 0x2f,
             0xd1, 0x69,
                0x53, 0x61, 0x6e, 0x20, 0x44, 0x69, 0x65, 0x67, 0x6f,
@@ -2249,21 +2803,46 @@
       0xcb, 0xc8, 0xc7, 0x24,
       0xc8, 0x22};
 
+
+static uint8_t spSpiffyTagInput[] = {
+   0x9f, // Open indefinite array
+
+   0xc0, // tag for string date
+   0x6a, '1','9','8','5','-','0','4','-','1','2', // Date string
+
+   0x6a, '1','9','8','5','-','0','4','-','1','2', // Date string
+
+   0x4a, '1','9','8','5','-','0','4','-','1','2', // Date string in byte string
+
+   0xd8, 0x23, // tag for regex
+   0x6a, '1','9','8','5','-','0','4','-','1','2', // Date string
+
+   0xc0, // tag for string date
+   0x4a, '1','9','8','5','-','0','4','-','1','2', // Date string in byte string
+
+   0xff
+};
+
+
 static int32_t CheckCSRMaps(QCBORDecodeContext *pDC);
 
 
 int32_t OptTagParseTest()
 {
    QCBORDecodeContext DCtx;
-   QCBORItem Item;
+   QCBORItem          Item;
+   QCBORError         uError;
 
    QCBORDecode_Init(&DCtx,
-                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spOptTestInput),
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTagInput),
                     QCBOR_DECODE_MODE_NORMAL);
 
-   //-------------------------
-   // This text matches the magic number tag and the fraction tag
-   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+   /*
+    This test matches the magic number tag and the fraction tag
+    55799([...])
+    */
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError != QCBOR_SUCCESS) {
       return -2;
    }
    if(Item.uDataType != QCBOR_TYPE_ARRAY ||
@@ -2271,38 +2850,96 @@
       return -3;
    }
 
-   if(QCBORDecode_GetNext(&DCtx, &Item)) {
+   /*
+    4([1,3])
+    */
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+#ifdef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+   if(uError != QCBOR_SUCCESS ||
+      Item.uDataType != QCBOR_TYPE_ARRAY ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_DECIMAL_FRACTION) ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 0) != CBOR_TAG_DECIMAL_FRACTION ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 1) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 2) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 3) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 4) != CBOR_TAG_INVALID64 ||
+      Item.val.uCount != 2) {
       return -4;
    }
+   // consume the items in the array
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
 
-#ifdef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
-   if(Item.uDataType != QCBOR_TYPE_ARRAY ||
-      !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_DECIMAL_FRACTION) ||
-      Item.val.uCount != 2) {
-      return -5;
-   }
 #else
-   if(Item.uDataType != QCBOR_TYPE_DECIMAL_FRACTION) {
-      return -6;
+   if(uError != QCBOR_SUCCESS ||
+      Item.uDataType != QCBOR_TYPE_DECIMAL_FRACTION ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 0) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 1) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 2) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 3) != CBOR_TAG_INVALID64 ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 4) != CBOR_TAG_INVALID64 ) {
+      return -5;
    }
 #endif
 
-   // --------------------------------
-   // 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)) {
+   /*
+    More than 4 tags on an item 225(226(227(228(229([])))))
+    */
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError != QCBOR_ERR_TOO_MANY_TAGS) {
       return -6;
    }
-   if(Item.uTagBits) {
+
+   /* tag 10489608748473423768(
+             2442302356(
+                21590(
+                   240(
+                      []))))
+    */
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError != QCBOR_SUCCESS ||
+      Item.uDataType != QCBOR_TYPE_ARRAY ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 3) != 10489608748473423768ULL ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 2) != 2442302356ULL ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 1) != 21590ULL ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 0) != 240ULL) {
       return -7;
    }
 
+   /* tag 21590(
+             10489608748473423768(
+                2442302357(
+                   21591(
+                       []))))
+    */
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError != QCBOR_SUCCESS ||
+      Item.uDataType != QCBOR_TYPE_ARRAY ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 0) != 65534ULL ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 1) != 2442302357ULL ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 2) != 10489608748473423768ULL ||
+      QCBORDecode_GetNthTag(&DCtx, &Item, 3) != 21590ULL) {
+      return -8;
+   }
+
+   /* Make sure to blow past the limit of tags that must be mapped.
+      works in conjuntion with entries above.
+    269488144(269488145(269488146(269488147([]))))
+    */
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError != QCBOR_ERR_TOO_MANY_TAGS) {
+      return -9;
+   }
+
+   uError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uError == QCBOR_SUCCESS) {
+      return -10;
+   }
+
    // ----------------------------------
    // This test sets up a caller-config list that includes the very large
-   // tage and then matches it.
+   // tage and then matches it. Caller-config lists are no longer
+   // used or needed. This tests backwards compatibility with them.
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEncodedLargeTag),
                     QCBOR_DECODE_MODE_NORMAL);
@@ -2323,6 +2960,7 @@
 
    //------------------------
    // Sets up a caller-configured list and look up something not in it
+   // Another backwards compatibility test.
    const uint64_t puLongList[17] = {1,2,1};
    const QCBORTagListIn TLLong = {17, puLongList};
    QCBORDecode_Init(&DCtx,
@@ -2333,13 +2971,14 @@
       return -11;
    }
 
-   // -----------------------
+   uint64_t puTags[4];
+   QCBORTagListOut Out = {0, 4, puTags};
+
+
    // 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;
    }
@@ -2351,7 +2990,7 @@
    }
 
    // ----------------------
-   // This text if too small of an out list
+   // This tests too small of an out list
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLotsOfTags),
                     QCBOR_DECODE_MODE_NORMAL);
@@ -2360,8 +2999,12 @@
       return -14;
    }
 
+
+
    // ---------------
-   // Parse a version of the "CSR" that has had a ton of tags randomly inserted
+   // Decode a version of the "CSR" that has had a ton of tags randomly inserted
+   // It is a bit of a messy test and maybe could be improved, but
+   // it is retained as a backwards compatibility check.
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRWithTags),
                     QCBOR_DECODE_MODE_NORMAL);
@@ -2375,6 +3018,11 @@
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCSRWithTags),
                     QCBOR_DECODE_MODE_NORMAL);
 
+   /* With the spiffy decode revision, this tag list is not used.
+    It doesn't matter if a tag is in this list or not so some
+    tests that couldn't process a tag because it isn't in this list
+    now can process these unlisted tags. The tests have been
+    adjusted for this. */
    const uint64_t puTagList[] = {773, 1, 90599561};
    const QCBORTagListIn TagList = {3, puTagList};
    QCBORDecode_SetCallerConfiguredTagList(&DCtx, &TagList);
@@ -2401,7 +3049,7 @@
    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
+      !QCBORDecode_IsTagged(&DCtx, &Item, 7) ||
       Item.val.uCount != 2 ||
       puTags[0] != 5859837686836516696 ||
       puTags[1] != 7 ||
@@ -2413,7 +3061,6 @@
       return -104;
    }
    if(Item.uDataType != QCBOR_TYPE_MAP ||
-      Item.uTagBits ||
       Item.val.uCount != 5 ||
       puTags[0] != 0x0b ||
       Out.uNumUsed != 1) {
@@ -2451,8 +3098,8 @@
       !QCBORDecode_IsTagged(&DCtx, &Item, 16) ||
       Item.val.string.len != 9 ||
       puTags[0] != 16 ||
-      puTags[11] != 0x0f ||
-      Out.uNumUsed != 12) {
+      puTags[3] != 7 ||
+      Out.uNumUsed != 4) {
       return -110;
    }
 
@@ -2482,7 +3129,7 @@
       return -113;
    }
    if(Item.uDataType != QCBOR_TYPE_MAP ||
-      QCBORDecode_IsTagged(&DCtx, &Item, 19) ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 19) ||
       Item.val.uCount != 2 ||
       puTags[0] != 19 ||
       Out.uNumUsed != 1) {
@@ -2493,8 +3140,7 @@
       return -115;
    }
    if(Item.uDataType != QCBOR_TYPE_MAP ||
-      QCBORDecode_IsTagged(&DCtx, &Item, 9) ||
-      Item.uTagBits ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 9) ||
       Item.val.uCount != 1 ||
       puTags[0] != 9 ||
       Out.uNumUsed != 1) {
@@ -2506,7 +3152,6 @@
    }
    if(Item.uDataType != QCBOR_TYPE_INT64 ||
       Item.val.int64 != -7 ||
-      Item.uTagBits ||
       Out.uNumUsed != 0) {
       return -117;
    }
@@ -2516,7 +3161,6 @@
    }
    if(Item.uDataType != QCBOR_TYPE_BYTE_STRING ||
       Item.val.string.len != 10 ||
-      Item.uTagBits ||
       puTags[0] != 12 ||
       Out.uNumUsed != 1) {
       return -119;
@@ -2537,7 +3181,7 @@
       return -122;
    }
    if(Item.uDataType != QCBOR_TYPE_INT64 ||
-      QCBORDecode_IsTagged(&DCtx, &Item, 8) ||
+      !QCBORDecode_IsTagged(&DCtx, &Item, 8) ||
       Item.val.int64 != -3 ||
       puTags[0] != 8 ||
       Out.uNumUsed != 1) {
@@ -2548,6 +3192,107 @@
       return -124;
    }
 
+   UsefulBufC DateString;
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSpiffyTagInput),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   // tagged date string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, &DateString);
+   // untagged date string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_SUCCESS) {
+      return 100;
+   }
+   // untagged byte string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 101;
+   }
+   // tagged regex
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 102;
+   }
+   // tagged date string with a byte string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_BAD_OPT_TAG) {
+      return 103;
+   }
+   QCBORDecode_ExitArray(&DCtx);
+   if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
+      return 104;
+   }
+
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSpiffyTagInput),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   // tagged date string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &DateString);
+   // untagged date string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_SUCCESS) {
+      return 200;
+   }
+   // untagged byte string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 201;
+   }
+   // tagged regex
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 202;
+   }
+   // tagged date string with a byte string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_BAD_OPT_TAG) {
+      return 203;
+   }
+   QCBORDecode_ExitArray(&DCtx);
+   if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
+      return 204;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSpiffyTagInput),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   // tagged date string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 300;
+   }
+   // untagged date string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 301;
+   }
+   // untagged byte string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 302;
+   }
+   // tagged regex
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 303;
+   }
+   // tagged date string with a byte string
+   QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &DateString);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_BAD_OPT_TAG) {
+      return 304;
+   }
+   QCBORDecode_ExitArray(&DCtx);
+   if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
+      return 305;
+   }
+
    return 0;
 }
 
@@ -2587,64 +3332,64 @@
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
       return -1;
    if(Item.uDataType != QCBOR_TYPE_ARRAY) {
-      return -1;
+      return -2;
    }
 
    //
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -3;
    if(Item.uDataType != QCBOR_TYPE_POSBIGNUM ||
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
-      return -1;
+      return -4;
    }
 
    //
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -5;
    if(Item.uDataType != QCBOR_TYPE_NEGBIGNUM ||
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
-      return -1;
+      return -6;
    }
 
    //
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -7;
    if(Item.uDataType != QCBOR_TYPE_MAP) {
-      return -1;
+      return -8;
    }
 
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -9;
    if(Item.uDataType != QCBOR_TYPE_POSBIGNUM ||
       Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
-      return -1;
+      return -10;
    }
 
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -11;
    if(Item.uDataType != QCBOR_TYPE_POSBIGNUM ||
       Item.uLabelType != QCBOR_TYPE_INT64 ||
       Item.label.int64 != 64 ||
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
-      return -1;
+      return -12;
    }
 
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -13;
    if(Item.uDataType != QCBOR_TYPE_NEGBIGNUM ||
       Item.uLabelType != QCBOR_TYPE_TEXT_STRING ||
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
-      return -1;
+      return -14;
    }
 
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -15;
    if(Item.uDataType != QCBOR_TYPE_NEGBIGNUM ||
       Item.uLabelType != QCBOR_TYPE_INT64 ||
       Item.label.int64 != -64 ||
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
-      return -1;
+      return -16;
    }
 
    return 0;
@@ -2690,26 +3435,26 @@
 {
    if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 0, 1, 0, NULL)) return -1;
 
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 1, 2, -23, NULL)) return -1;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 1, 2, -23, NULL)) return -2;
 
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 2, 3, -20, NULL)) return -1;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 2, 3, -20, NULL)) return -3;
 
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -18, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -17, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -15, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -16, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 2, -14, NULL)) return -1;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -18, NULL)) return -4;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -17, NULL)) return -5;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -15, NULL)) return -6;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 3, -16, NULL)) return -7;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_TEXT_STRING, 3, 2, -14, NULL)) return -8;
 
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 2, 3, -19, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 3, 4, -11, NULL)) return -1;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 2, 3, -19, NULL)) return -9;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 3, 4, -11, NULL)) return -10;
 
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_INT64, 4, 3, -9, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_BYTE_STRING, 3, 1, -10, NULL)) return -1;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_INT64, 4, 3, -9, NULL)) return -11;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_BYTE_STRING, 3, 1, -10, NULL)) return -12;
 
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 1, 2, -22, NULL)) return -1;
-   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_INT64, 2, 0, -5, NULL)) return -1;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_MAP, 1, 2, -22, NULL)) return -13;
+   if(CheckItemWithIntLabel(pDC, QCBOR_TYPE_INT64, 2, 0, -5, NULL)) return -14;
 
-   if(QCBORDecode_Finish(pDC)) return -2;
+   if(QCBORDecode_Finish(pDC)) return -20;
 
    return 0;
 }
@@ -2845,7 +3590,7 @@
       QCBORError nReturn = QCBORDecode_GetNext(&DC, &Item);
       if(j >= QCBOR_MAX_ARRAY_NESTING) {
          // Should be in error
-         if(nReturn != QCBOR_ERR_ARRAY_NESTING_TOO_DEEP) {
+         if(nReturn != QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP) {
             return -4;
          } else {
             return 0; // Decoding doesn't recover after an error
@@ -2963,7 +3708,7 @@
    }
 
    nResult = QCBORDecode_Finish(&DC);
-   if(nResult != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+   if(nResult != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
       return -8;
    }
 
@@ -2991,7 +3736,7 @@
    }
 
    nResult = QCBORDecode_Finish(&DC);
-   if(nResult != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+   if(nResult != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
       return -12;
    }
 
@@ -3009,10 +3754,15 @@
    }
 
    nResult = QCBORDecode_GetNext(&DC, &Item);
-   if(nResult != QCBOR_ERR_BAD_BREAK) {
+   if(nResult != QCBOR_SUCCESS) {
       return -14;
    }
 
+   nResult = QCBORDecode_GetNext(&DC, &Item);
+    if(nResult != QCBOR_ERR_BAD_BREAK) {
+       return -140;
+    }
+
 
    // --- next test -----
    IndefLen = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefiniteArrayBad4);
@@ -3032,7 +3782,7 @@
    }
 
    nResult = QCBORDecode_Finish(&DC);
-   if(nResult != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+   if(nResult != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
       return -17;
    }
 
@@ -3356,7 +4106,8 @@
       return -2;
    }
 
-   // Next parse, save pointers to a few strings, destroy original and see all is OK.
+   // Next parse, save pointers to a few strings, destroy original and
+   // see all is OK.
    UsefulBuf_MAKE_STACK_UB(CopyOfStorage, sizeof(pValidMapEncoded) + QCBOR_DECODE_MIN_MEM_POOL_SIZE);
    const UsefulBufC CopyOf = UsefulBuf_Copy(CopyOfStorage, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded));
 
@@ -3386,13 +4137,13 @@
       Item1.val.int64 != 42 ||
       Item1.uDataAlloc != 0 ||
       Item1.uLabelAlloc == 0 ||
-      UsefulBuf_Compare(Item1.label.string, UsefulBuf_FromSZ("first integer"))) {
+      UsefulBufCompareToSZ(Item1.label.string, "first integer")) {
       return -4;
    }
 
 
    if(Item2.uLabelType != QCBOR_TYPE_TEXT_STRING ||
-      UsefulBuf_Compare(Item2.label.string, UsefulBuf_FromSZ("an array of two strings")) ||
+      UsefulBufCompareToSZ(Item2.label.string, "an array of two strings") ||
       Item2.uDataType != QCBOR_TYPE_ARRAY ||
       Item2.uDataAlloc != 0 ||
       Item2.uLabelAlloc == 0 ||
@@ -3402,14 +4153,14 @@
    if(Item3.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item3.uDataAlloc == 0 ||
       Item3.uLabelAlloc != 0 ||
-      UsefulBuf_Compare(Item3.val.string, UsefulBuf_FromSZ("string1"))) {
+      UsefulBufCompareToSZ(Item3.val.string, "string1")) {
       return -6;
    }
 
    if(Item4.uDataType != QCBOR_TYPE_TEXT_STRING ||
       Item4.uDataAlloc == 0 ||
       Item4.uLabelAlloc != 0 ||
-      UsefulBuf_Compare(Item4.val.string, UsefulBuf_FromSZ("string2"))) {
+      UsefulBufCompareToSZ(Item4.val.string, "string2")) {
       return -7;
    }
 
@@ -3572,23 +4323,23 @@
    return 0;
 }
 
-
 #ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
-/*
+
+/*  exponent, mantissa
   [
     4([-1, 3]),
-    4([-20, 4759477275222530853136]),
-    4([9223372036854775807, -4759477275222530853137]),
+    4([-20,                   4759477275222530853136]),
+    4([9223372036854775807,  -4759477275222530853137]),
     5([300, 100]),
-    5([-20, 4759477275222530853136]),
+    5([-20,                   4759477275222530853136]),
     5([-9223372036854775807, -4759477275222530853137])
-    5([9223372036854775806, -4759477275222530853137])
-    5([9223372036854775806, 9223372036854775806])]
+    5([ 9223372036854775806, -4759477275222530853137])
+    5([ 9223372036854775806,  9223372036854775806])]
  ]
  */
 
 static const uint8_t spExpectedExponentsAndMantissas[] = {
-   0x87,
+   0x88,
    0xC4, 0x82, 0x20,
                0x03,
    0xC4, 0x82, 0x33,
@@ -3601,14 +4352,17 @@
                0xC2, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
    0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
                0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+   0xC5, 0x82, 0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+               0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
    0xC5, 0x82, 0x1B, 0x7f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
                0x1B, 0x7f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE
 };
 
+
 int32_t ExponentAndMantissaDecodeTests(void)
 {
    QCBORDecodeContext DC;
-   QCBORError         nCBORError;
+   QCBORError         uErr;
    QCBORItem          item;
 
    static const uint8_t spBigNumMantissa[] = {0x01, 0x02, 0x03, 0x04, 0x05,
@@ -3616,10 +4370,12 @@
    UsefulBufC BN = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNumMantissa);
 
 
-   QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentsAndMantissas), QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_Init(&DC,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentsAndMantissas),
+                    QCBOR_DECODE_MODE_NORMAL);
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 1;
    }
 
@@ -3627,8 +4383,8 @@
       return 2;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 3;
    }
 
@@ -3638,8 +4394,8 @@
       return 4;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 5;
    }
 
@@ -3649,8 +4405,8 @@
       return 6;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 7;
    }
 
@@ -3660,8 +4416,8 @@
       return 8;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 9;
    }
 
@@ -3671,37 +4427,53 @@
       return 10;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   // 5([-20, 4759477275222530853136]),
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 11;
    }
-
    if(item.uDataType != QCBOR_TYPE_BIGFLOAT_POS_BIGNUM ||
       item.val.expAndMantissa.nExponent != -20 ||
       UsefulBuf_Compare(item.val.expAndMantissa.Mantissa.bigNum, BN)) {
       return 12;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   // 5([-9223372036854775807, -4759477275222530853137])
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 13;
    }
-
    if(item.uDataType != QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM ||
       item.val.expAndMantissa.nExponent != -9223372036854775807 ||
       UsefulBuf_Compare(item.val.expAndMantissa.Mantissa.bigNum, BN)) {
       return 14;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
+   // 5([ 9223372036854775806, -4759477275222530853137])
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
       return 15;
    }
+   if(item.uDataType != QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM ||
+      item.val.expAndMantissa.nExponent != 9223372036854775806 ||
+      UsefulBuf_Compare(item.val.expAndMantissa.Mantissa.bigNum, BN)) {
+      return 16;
+   }
 
+   // 5([ 9223372036854775806,  9223372036854775806])]
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
+      return 17;
+   }
    if(item.uDataType != QCBOR_TYPE_BIGFLOAT ||
       item.val.expAndMantissa.nExponent != 9223372036854775806 ||
       item.val.expAndMantissa.Mantissa.nInt!= 9223372036854775806 ) {
-      return 16;
+      return 18;
+   }
+
+   uErr = QCBORDecode_Finish(&DC);
+   if(uErr != QCBOR_SUCCESS) {
+      return 18;
    }
 
    /* Now encode some stuff and then decode it */
@@ -3719,42 +4491,90 @@
 
 
    QCBORDecode_Init(&DC, Encoded, QCBOR_DECODE_MODE_NORMAL);
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
-      return 13;
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
+      return 100;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
-      return 13;
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
+      return 101;
    }
 
    if(item.uDataType != QCBOR_TYPE_DECIMAL_FRACTION ||
       item.val.expAndMantissa.nExponent != 1000 ||
       item.val.expAndMantissa.Mantissa.nInt != 999) {
-      return 15;
+      return 102;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
-      return 13;
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
+      return 103;
    }
 
    if(item.uDataType != QCBOR_TYPE_BIGFLOAT ||
       item.val.expAndMantissa.nExponent != INT32_MIN ||
       item.val.expAndMantissa.Mantissa.nInt != 100) {
-      return 15;
+      return 104;
    }
 
-   nCBORError = QCBORDecode_GetNext(&DC, &item);
-   if(nCBORError != QCBOR_SUCCESS) {
-      return 13;
+   uErr = QCBORDecode_GetNext(&DC, &item);
+   if(uErr != QCBOR_SUCCESS) {
+      return 105;
    }
 
    if(item.uDataType != QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM ||
       item.val.expAndMantissa.nExponent != INT32_MAX ||
       UsefulBuf_Compare(item.val.expAndMantissa.Mantissa.bigNum, BN)) {
-      return 12;
+      return 106;
+   }
+
+
+   int64_t                   nExp, nMant;
+   UsefulBuf_MAKE_STACK_UB(  MantBuf, 20);
+   UsefulBufC                Mant;
+   bool                      bIsNeg;
+
+   QCBORDecode_Init(&DC,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentsAndMantissas),
+                    QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_EnterArray(&DC, NULL);
+
+   // 4([-1, 3]),
+   QCBORDecode_GetDecimalFraction(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nExp, &nMant);
+
+   // 4([-20,                   4759477275222530853136]),
+   QCBORDecode_GetDecimalFractionBig(&DC, QCBOR_TAG_REQUIREMENT_TAG, MantBuf,
+                                     &Mant, &bIsNeg, &nExp);
+
+   // 4([9223372036854775807,  -4759477275222530853137]),
+   QCBORDecode_GetDecimalFractionBig(&DC, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG,
+                                     MantBuf, &Mant, &bIsNeg, &nExp);
+
+   // 5([300, 100]),
+   QCBORDecode_GetBigFloat(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nExp, &nMant);
+
+   // 5([-20,                   4759477275222530853136]),
+   QCBORDecode_GetBigFloatBig(&DC, QCBOR_TAG_REQUIREMENT_TAG, MantBuf, &Mant,
+                              &bIsNeg, &nExp);
+
+   // 5([-9223372036854775807, -4759477275222530853137])
+   QCBORDecode_GetBigFloatBig(&DC, QCBOR_TAG_REQUIREMENT_TAG, MantBuf, &Mant,
+                              &bIsNeg, &nExp);
+
+   // 5([ 9223372036854775806, -4759477275222530853137])
+   QCBORDecode_GetBigFloatBig(&DC, QCBOR_TAG_REQUIREMENT_TAG, MantBuf, &Mant,
+                              &bIsNeg, &nExp);
+
+   // 5([ 9223372036854775806,  9223372036854775806])]
+   QCBORDecode_GetBigFloatBig(&DC, QCBOR_TAG_REQUIREMENT_TAG, MantBuf, &Mant,
+                              &bIsNeg, &nExp);
+
+   QCBORDecode_ExitArray(&DC);
+
+   uErr = QCBORDecode_Finish(&DC);
+   if(uErr != QCBOR_SUCCESS) {
+      return 200;
    }
 
    return 0;
@@ -3771,9 +4591,9 @@
                   0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05,
                   0x06, 0x07, 0x08, 0x09, 0x10}, 23}, QCBOR_ERR_BAD_EXP_AND_MANTISSA},
    // End of input
-   { {(uint8_t[]){0xC4, 0x82}, 2}, QCBOR_ERR_HIT_END},
+   { {(uint8_t[]){0xC4, 0x82}, 2}, QCBOR_ERR_NO_MORE_ITEMS},
    // End of input
-   { {(uint8_t[]){0xC4, 0x82, 0x01}, 3}, QCBOR_ERR_HIT_END},
+   { {(uint8_t[]){0xC4, 0x82, 0x01}, 3}, QCBOR_ERR_NO_MORE_ITEMS},
    // bad content for big num
    { {(uint8_t[]){0xC4, 0x82, 0x01, 0xc3, 0x01}, 5}, QCBOR_ERR_BAD_OPT_TAG},
    // bad content for big num
@@ -3806,6 +4626,1084 @@
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 
 
+
+/*
+ Some basic CBOR with map and array used in a lot of tests.
+ The map labels are all strings
+
+ {
+  "first integer": 42,
+  "an array of two strings": [
+      "string1", "string2"
+  ],
+  "map in a map": {
+      "bytes 1": h'78787878',
+      "bytes 2": h'79797979',
+      "another int": 98,
+      "text 2": "lies, damn lies and statistics"
+   }
+  }
+ */
+
+int32_t SpiffyDecodeBasicMap(UsefulBufC input)
+{
+     QCBORItem Item1, Item2, Item3;
+     int64_t nDecodedInt1, nDecodedInt2;
+     UsefulBufC B1, B2, S1, S2, S3;
+
+     QCBORDecodeContext DCtx;
+     QCBORError nCBORError;
+
+     QCBORDecode_Init(&DCtx, input, 0);
+
+     QCBORDecode_EnterMap(&DCtx, NULL);
+
+        QCBORDecode_GetInt64InMapSZ(&DCtx, "first integer",  &nDecodedInt1);
+
+        QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+           QCBORDecode_GetInt64InMapSZ(&DCtx,  "another int",  &nDecodedInt2);
+           QCBORDecode_GetByteStringInMapSZ(&DCtx, "bytes 1",  &B1);
+           QCBORDecode_GetByteStringInMapSZ(&DCtx, "bytes 2",  &B2);
+           QCBORDecode_GetTextStringInMapSZ(&DCtx, "text 2",  &S1);
+        QCBORDecode_ExitMap(&DCtx);
+
+        QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+           QCBORDecode_GetNext(&DCtx, &Item1);
+           QCBORDecode_GetNext(&DCtx, &Item2);
+           if(QCBORDecode_GetNext(&DCtx, &Item3) != QCBOR_ERR_NO_MORE_ITEMS) {
+              return -400;
+           }
+        QCBORDecode_ExitArray(&DCtx);
+
+        // Parse the same array again using GetText() instead of GetItem()
+        QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+           QCBORDecode_GetTextString(&DCtx, &S2);
+           QCBORDecode_GetTextString(&DCtx, &S3);
+           if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+              return 5000;
+           }
+      /*     QCBORDecode_GetText(&DCtx, &S3);
+           if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_NO_MORE_ITEMS) {
+               return 5001;
+           } */
+
+        QCBORDecode_ExitArray(&DCtx);
+
+     QCBORDecode_ExitMap(&DCtx);
+
+     nCBORError = QCBORDecode_Finish(&DCtx);
+
+     if(nCBORError) {
+        return (int32_t)nCBORError;
+     }
+
+     if(nDecodedInt1 != 42) {
+        return 1001;
+     }
+
+     if(nDecodedInt2 != 98) {
+        return 1002;
+     }
+
+     if(Item1.uDataType != QCBOR_TYPE_TEXT_STRING ||
+        UsefulBufCompareToSZ(Item1.val.string, "string1")) {
+        return 1003;
+     }
+
+     if(Item1.uDataType != QCBOR_TYPE_TEXT_STRING ||
+        UsefulBufCompareToSZ(Item2.val.string, "string2")) {
+        return 1004;
+     }
+
+     if(UsefulBufCompareToSZ(S1, "lies, damn lies and statistics")) {
+        return 1005;
+     }
+
+     if(UsefulBuf_Compare(B1, UsefulBuf_FromSZ("xxxx"))){
+        return 1006;
+     }
+
+     if(UsefulBuf_Compare(B2, UsefulBuf_FromSZ("yyyy"))){
+        return 1007;
+     }
+
+     if(UsefulBuf_Compare(S2, UsefulBuf_FromSZ("string1"))){
+        return 1008;
+     }
+
+     if(UsefulBuf_Compare(S3, UsefulBuf_FromSZ("string2"))){
+        return 1009;
+     }
+
+   return 0;
+}
+
+
+/*
+ [23,
+  6000,
+  h'67616C6163746963',
+  h'686176656E20746F6B656E'
+ ]
+ */
+static const uint8_t spSimpleArray[] = {
+   0x84,
+   0x17,
+   0x19, 0x17, 0x70,
+   0x48, 0x67, 0x61, 0x6C, 0x61, 0x63, 0x74, 0x69, 0x63,
+   0x4B, 0x68, 0x61, 0x76, 0x65, 0x6E, 0x20, 0x74, 0x6F, 0x6B, 0x65, 0x6E};
+
+
+static const uint8_t spEmptyMap[] = {0xa0};
+
+static const uint8_t spEmptyInDefinteLengthMap[] = {0xbf, 0xff};
+
+static const uint8_t spArrayOfEmpty[] = {0x84, 0x40, 0xa0, 0x80, 0x00};
+
+/*
+   {
+      0: [],
+      9: [
+         [],
+         []
+      ],
+      8: {
+         1: [],
+         2: {},
+         3: []
+      },
+      4: {},
+      5: [],
+      6: [
+         [],
+         []
+      ]
+ }
+ */
+
+static const uint8_t spMapOfEmpty[] = {
+   0xa6, 0x00, 0x80, 0x09, 0x82, 0x80, 0x80, 0x08, 0xa3, 0x01,
+   0x80, 0x02, 0xa0, 0x03, 0x80, 0x04, 0xa0, 0x05, 0x9f, 0xff,
+   0x06, 0x9f, 0x80, 0x9f, 0xff, 0xff};
+
+/*
+ Too many tags
+ Invalid tag content
+ Duplicate label
+ Integer overflow
+ Date overflow
+
+ {1: 224(225(226(227(4(0))))),
+  2: 1(h''),
+  3: -18446744073709551616,
+  4: 1(1.0e+300),
+  5: 0, 8: 8}
+ */
+static const uint8_t spRecoverableMapErrors[] = {
+   0xbf,
+   0x01, 0xd8, 0xe0, 0xd8, 0xe1, 0xd8, 0xe2, 0xd8, 0xe3, 0xd8, 0x04, 0x00,
+   0x02, 0xc1, 0x40,
+   0x03, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0x04, 0xc1, 0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c,
+   0x05, 0x00,
+   0x05, 0x00,
+   0x08, 0x08,
+   0xff
+};
+
+// Bad break
+static const uint8_t spUnRecoverableMapError1[] = {
+   0xa2, 0xff, 0x01, 0x00, 0x02, 0x00
+};
+
+// No more items
+static const uint8_t spUnRecoverableMapError2[] = {
+   0xbf, 0x02, 0xbf, 0xff, 0x01, 0x00, 0x02, 0x00
+};
+
+// Hit end because string is too long
+static const uint8_t spUnRecoverableMapError3[] = {
+   0xbf, 0x02, 0x69, 0x64, 0x64, 0xff
+};
+
+// Hit end because string is too long
+static const uint8_t spUnRecoverableMapError4[] = {
+   0xbf,
+      0x02, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+            0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff
+};
+
+
+int32_t EnterMapTest()
+{
+   QCBORItem          Item1;
+   QCBORItem          ArrayItem;
+   QCBORDecodeContext DCtx;
+   int32_t            nReturn;
+   QCBORError         uErr;
+
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spMapOfEmpty), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+
+
+   QCBORDecode_EnterArray(&DCtx, NULL); // Label 0
+   QCBORDecode_ExitArray(&DCtx);
+
+   QCBORDecode_EnterArray(&DCtx, NULL); // Label 9
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_ExitArray(&DCtx);
+
+   QCBORDecode_EnterMap(&DCtx, NULL);  // Label 8
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_ExitMap(&DCtx);
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_ExitMap(&DCtx);
+
+   QCBORDecode_EnterMap(&DCtx, NULL);  // Label4
+   QCBORDecode_ExitMap(&DCtx);
+
+   QCBORDecode_EnterArray(&DCtx, NULL); // Label 5
+   QCBORDecode_ExitArray(&DCtx);
+
+   QCBORDecode_EnterArray(&DCtx, NULL); // Label 6
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_ExitArray(&DCtx);
+
+   QCBORDecode_ExitMap(&DCtx);
+
+   uErr = QCBORDecode_Finish(&DCtx);
+   if(uErr != QCBOR_SUCCESS){
+      return 3011;
+   }
+
+
+   (void)pValidMapIndefEncoded;
+   nReturn = SpiffyDecodeBasicMap(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapIndefEncoded));
+   if(nReturn) {
+      return nReturn + 20000;
+   }
+
+   nReturn = SpiffyDecodeBasicMap(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded));
+   if(nReturn) {
+      return nReturn;
+   }
+
+
+
+   // These tests confirm the cursor is at the right place after entering
+   // a map or array
+   const UsefulBufC ValidEncodedMap = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded);
+
+   // Confirm cursor is at right place
+   QCBORDecode_Init(&DCtx, ValidEncodedMap, 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   if(Item1.uDataType != QCBOR_TYPE_INT64) {
+      return 2001;
+   }
+
+
+   QCBORDecode_Init(&DCtx, ValidEncodedMap, 0);
+   QCBORDecode_VGetNext(&DCtx, &Item1);
+   QCBORDecode_VGetNext(&DCtx, &Item1);
+   QCBORDecode_EnterArray(&DCtx, &ArrayItem);
+   if(ArrayItem.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+      UsefulBuf_Compare(ArrayItem.label.string,
+                        UsefulBuf_FROM_SZ_LITERAL("an array of two strings"))) {
+      return 2051;
+   }
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   if(Item1.uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return 2002;
+   }
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_EnterMap(&DCtx, &ArrayItem);
+   if(ArrayItem.uLabelType != QCBOR_TYPE_TEXT_STRING ||
+      UsefulBuf_Compare(ArrayItem.label.string,
+                        UsefulBuf_FROM_SZ_LITERAL("map in a map"))) {
+      return 2052;
+   }
+
+
+   QCBORDecode_Init(&DCtx, ValidEncodedMap, 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   if(Item1.uDataType != QCBOR_TYPE_BYTE_STRING) {
+      return 2003;
+   }
+
+   QCBORDecode_Init(&DCtx, ValidEncodedMap, 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   if(Item1.uDataType != QCBOR_TYPE_TEXT_STRING) {
+      return 2004;
+   }
+
+   QCBORDecode_Init(&DCtx, ValidEncodedMap, 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   if(Item1.uDataType != QCBOR_TYPE_MAP && Item1.uLabelAlloc != QCBOR_TYPE_TEXT_STRING) {
+      return 2006;
+   }
+   QCBORDecode_ExitMap(&DCtx);
+   if(QCBORDecode_GetNext(&DCtx, &Item1) != QCBOR_ERR_NO_MORE_ITEMS) {
+      return 2007;
+   }
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSimpleArray), 0);
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   int64_t nDecodedInt2;
+   QCBORDecode_GetInt64InMapSZ(&DCtx, "another int",  &nDecodedInt2);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_MAP_NOT_ENTERED){
+      return 2008;
+   }
+   UsefulBufC String;
+   QCBORDecode_GetTextStringInMapN(&DCtx, 88, &String);
+   if(uErr != QCBOR_ERR_MAP_NOT_ENTERED){
+      return 2009;
+   }
+
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEmptyMap), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   // This will fail because the map is empty.
+   QCBORDecode_GetInt64InMapSZ(&DCtx, "another int",  &nDecodedInt2);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+      return 2010;
+   }
+   QCBORDecode_ExitMap(&DCtx);
+   uErr = QCBORDecode_Finish(&DCtx);
+   if(uErr != QCBOR_SUCCESS){
+      return 2011;
+   }
+
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEmptyInDefinteLengthMap), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   // This will fail because the map is empty.
+   QCBORDecode_GetInt64InMapSZ(&DCtx, "another int",  &nDecodedInt2);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_LABEL_NOT_FOUND){
+      return 2012;
+   }
+   QCBORDecode_ExitMap(&DCtx);
+   uErr = QCBORDecode_Finish(&DCtx);
+   if(uErr != QCBOR_SUCCESS){
+      return 2013;
+   }
+
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spArrayOfEmpty), 0);
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_GetByteString(&DCtx, &String);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_ExitMap(&DCtx);
+   QCBORDecode_EnterArray(&DCtx, NULL);
+   QCBORDecode_ExitArray(&DCtx);
+   QCBORDecode_GetInt64(&DCtx, &nDecodedInt2);
+   QCBORDecode_ExitArray(&DCtx);
+   uErr = QCBORDecode_Finish(&DCtx);
+   if(uErr != QCBOR_SUCCESS) {
+      return 2014;
+   }
+
+   int64_t nInt;
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spRecoverableMapErrors), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_TOO_MANY_TAGS) {
+      return 2021;
+   }
+
+   QCBORDecode_GetEpochDateInMapN(&DCtx, 0x02, QCBOR_TAG_REQUIREMENT_TAG, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_BAD_OPT_TAG) {
+      return 2022;
+   }
+
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x03, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_INT_OVERFLOW) {
+      return 2023;
+   }
+
+   QCBORDecode_GetEpochDateInMapN(&DCtx, 0x04, QCBOR_TAG_REQUIREMENT_TAG, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   if(uErr != QCBOR_ERR_DATE_OVERFLOW) {
+      return 2024;
+   }
+#else
+   if(uErr != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+      return 2027;
+   }
+#endif
+
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x05, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+      return 2025;
+   }
+
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x08, &nInt);
+
+   QCBORDecode_ExitMap(&DCtx);
+   uErr = QCBORDecode_Finish(&DCtx);
+   if(uErr != QCBOR_SUCCESS) {
+      return 2026;
+   }
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError1), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_BAD_BREAK) {
+      return 2030;
+   }
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError2), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_NO_MORE_ITEMS) {
+      return 2031;
+   }
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError3), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_HIT_END) {
+      return 2032;
+   }
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError4), 0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+   uErr = QCBORDecode_GetAndResetError(&DCtx);
+   if(uErr != QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP) {
+      return 2033;
+   }
+
+   return 0;
+}
+
+
+struct NumberConversion {
+   char       *szDescription;
+   UsefulBufC  CBOR;
+   int64_t     nConvertedToInt64;
+   QCBORError  uErrorInt64;
+   uint64_t    uConvertToUInt64;
+   QCBORError  uErrorUint64;
+   double      dConvertToDouble;
+   QCBORError  uErrorDouble;
+};
+
+static const struct NumberConversion NumberConversions[] = {
+   {
+      "too large to fit into int64_t",
+      {(uint8_t[]){0xc3, 0x48, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 10},
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      ((double)INT64_MIN) + 1 ,
+      QCBOR_SUCCESS
+   },
+   {
+      "largest negative int that fits in int64_t",
+      {(uint8_t[]){0xc3, 0x48, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 10},
+      INT64_MIN,
+      QCBOR_SUCCESS,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      (double)INT64_MIN,
+      QCBOR_SUCCESS
+   },
+   {
+      "negative bignum -1",
+      {(uint8_t[]){0xc3, 0x41, 0x00}, 3},
+      -1,
+      QCBOR_SUCCESS,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -1.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Decimal Fraction with positive bignum 257 * 10e3",
+      {(uint8_t[]){0xC4, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+                               0xC2, 0x42, 0x01, 0x01}, 15},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      257000,
+      QCBOR_SUCCESS,
+      257000,
+      QCBOR_SUCCESS,
+      257000.0,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA*/
+   },
+   {
+      "bigfloat with negative bignum -258 * 2e3",
+      {(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+                               0xC3, 0x42, 0x01, 0x01}, 15},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      -2064,
+      QCBOR_SUCCESS,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -2064.0,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA*/
+   },
+   {
+      "bigfloat with positive bignum 257 * 2e3",
+      {(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+                               0xC2, 0x42, 0x01, 0x01}, 15},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      2056,
+      QCBOR_SUCCESS,
+      2056,
+      QCBOR_SUCCESS,
+      2056.0,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA*/
+   },
+   {
+      "negative bignum 0xc349010000000000000000 -18446744073709551617",
+      {(uint8_t[]){0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 11},
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -18446744073709551617.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Positive bignum 0x01020304 indefinite length string",
+      {(uint8_t[]){0xC2, 0x5f, 0x42, 0x01, 0x02, 0x41, 0x03, 0x41, 0x04, 0xff}, 10},
+      0x01020304,
+      QCBOR_SUCCESS,
+      0x01020304,
+      QCBOR_SUCCESS,
+      16909060.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Decimal Fraction with neg bignum [9223372036854775807, -4759477275222530853137]",
+      {(uint8_t[]){0xC4, 0x82, 0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                               0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,}, 23},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -INFINITY,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+   },
+   {
+      "big float [9223372036854775806,  9223372036854775806]",
+      {(uint8_t[]){0xC5, 0x82, 0x1B, 0x7f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+                               0x1B, 0x7f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE}, 20},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      INFINITY,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+   },
+   {
+      "Big float 3 * 2^^2",
+      {(uint8_t[]){0xC5, 0x82, 0x02, 0x03}, 4},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      12,
+      QCBOR_SUCCESS,
+      12,
+      QCBOR_SUCCESS,
+      12.0,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+   },
+   {
+      "Positive integer 18446744073709551615",
+      {(uint8_t[]){0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 9},
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      18446744073709551615ULL,
+      QCBOR_SUCCESS,
+      18446744073709551615.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Positive bignum 0xffff",
+      {(uint8_t[]){0xC2, 0x42, 0xff, 0xff}, 4},
+      65536-1,
+      QCBOR_SUCCESS,
+      0xffff,
+      QCBOR_SUCCESS,
+      65535.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Postive integer 0",
+      {(uint8_t[]){0x0}, 1},
+      0LL,
+      QCBOR_SUCCESS,
+      0ULL,
+      QCBOR_SUCCESS,
+      0.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Negative integer -18446744073709551616",
+      {(uint8_t[]){0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 9},
+      -9223372036854775807-1, // INT64_MIN
+      QCBOR_SUCCESS,
+      0ULL,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -9223372036854775808.0,
+      QCBOR_SUCCESS
+   },
+   {
+      "Double Floating point value 100.3",
+      {(uint8_t[]){0xfb, 0x40, 0x59, 0x13, 0x33, 0x33, 0x33, 0x33, 0x33}, 9},
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      100L,
+      QCBOR_SUCCESS,
+      100ULL,
+      QCBOR_SUCCESS,
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+      100.3,
+      QCBOR_SUCCESS
+   },
+   {
+      "Floating point value NaN 0xfa7fc00000",
+      {(uint8_t[]){0xfa, 0x7f, 0xc0, 0x00, 0x00}, 5},
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      0,
+      QCBOR_ERR_FLOAT_EXCEPTION,
+      0,
+      QCBOR_ERR_FLOAT_EXCEPTION,
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+      NAN,
+      QCBOR_SUCCESS
+   },
+   {
+      "half-precision Floating point value -4",
+      {(uint8_t[]){0xf9, 0xc4, 0x00}, 3},
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      // Normal case with all enabled.
+      -4,
+      QCBOR_SUCCESS,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -4.0,
+      QCBOR_SUCCESS
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+      // Float HW disabled
+      -4,
+      QCBOR_ERR_HW_FLOAT_DISABLED, // Can't convert to integer
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED, // Can't convert to integer
+      -4.0,
+      QCBOR_SUCCESS // Uses ieee754.h to conver, not HW
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#else
+      // Half-precision disabled
+      -4,
+      QCBOR_ERR_HALF_PRECISION_DISABLED,
+      0,
+      QCBOR_ERR_HALF_PRECISION_DISABLED,
+      -4.0,
+      QCBOR_ERR_HALF_PRECISION_DISABLED
+#endif
+   },
+   {
+      "Decimal fraction 3/10",
+      {(uint8_t[]){0xC4, 0x82, 0x20, 0x03}, 4},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0.30000000000000004,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+   },
+   {
+      "+inifinity",
+      {(uint8_t[]){0xfa, 0x7f, 0x80, 0x00, 0x00}, 5},
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      0,
+      QCBOR_ERR_FLOAT_EXCEPTION,
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+      INFINITY,
+      QCBOR_SUCCESS
+   },
+
+   {
+      "extreme pos bignum",
+      {(uint8_t[]){0xc2, 0x59, 0x01, 0x90,
+         // 50 rows of 8 is 400 digits.
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0},
+         404},
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      INFINITY,
+      QCBOR_SUCCESS
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+   },
+
+   {
+      "extreme neg bignum",
+      {(uint8_t[]){0xc3, 0x59, 0x01, 0x90,
+         // 50 rows of 8 is 400 digits.
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0},
+         404},
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      -INFINITY,
+      QCBOR_SUCCESS
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+      0,
+      QCBOR_ERR_HW_FLOAT_DISABLED,
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+   },
+
+   {
+      "big float underflow [9223372036854775806, -9223372036854775806]",
+      {(uint8_t[]){
+         0xC5, 0x82,
+            0x3B, 0x7f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+            0x1B, 0x7f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE}, 20},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+   },
+
+   {
+      "bigfloat that evaluates to -INFINITY",
+      {(uint8_t[]){
+         0xC5, 0x82,
+            0x1B, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+            0xC3, 0x42, 0x01, 0x01}, 15},
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+      0,
+      QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+      0,
+      QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+      -INFINITY,
+      QCBOR_SUCCESS
+#else /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0,
+      QCBOR_ERR_UNEXPECTED_TYPE,
+      0.0,
+      QCBOR_ERR_UNEXPECTED_TYPE
+#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA*/
+   },
+};
+
+
+
+int32_t IntegerConvertTest()
+{
+   const int nNumTests = sizeof(NumberConversions)/sizeof(struct NumberConversion);
+
+   for(int nIndex = 0; nIndex < nNumTests; nIndex++) {
+      const struct NumberConversion *pF = &NumberConversions[nIndex];
+
+      // Set up the decoding context including a memory pool so that
+      // indefinite length items can be checked
+      QCBORDecodeContext DCtx;
+      UsefulBuf_MAKE_STACK_UB(Pool, 100);
+
+      /* ----- test conversion to int64_t ------ */
+      QCBORDecode_Init(&DCtx, pF->CBOR, QCBOR_DECODE_MODE_NORMAL);
+      QCBORError nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
+      if(nCBORError) {
+         return (int32_t)(1000+nIndex);
+      }
+
+      int64_t nInt;
+      QCBORDecode_GetInt64ConvertAll(&DCtx, 0xffff, &nInt);
+      if(QCBORDecode_GetError(&DCtx) != pF->uErrorInt64) {
+         return (int32_t)(2000+nIndex);
+      }
+      if(pF->uErrorInt64 == QCBOR_SUCCESS && pF->nConvertedToInt64 != nInt) {
+         return (int32_t)(3000+nIndex);
+      }
+
+      /* ----- test conversion to uint64_t ------ */
+      QCBORDecode_Init(&DCtx, pF->CBOR, QCBOR_DECODE_MODE_NORMAL);
+      nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
+      if(nCBORError) {
+         return (int32_t)(1000+nIndex);
+      }
+      uint64_t uInt;
+      QCBORDecode_GetUInt64ConvertAll(&DCtx, 0xffff, &uInt);
+      if(QCBORDecode_GetError(&DCtx) != pF->uErrorUint64) {
+         return (int32_t)(4000+nIndex);
+      }
+      if(pF->uErrorUint64 == QCBOR_SUCCESS && pF->uConvertToUInt64 != uInt) {
+         return (int32_t)(5000+nIndex);
+      }
+
+      /* ----- test conversion to double ------ */
+      QCBORDecode_Init(&DCtx, pF->CBOR, QCBOR_DECODE_MODE_NORMAL);
+      nCBORError = QCBORDecode_SetMemPool(&DCtx, Pool, 0);
+      if(nCBORError) {
+         return (int32_t)(1000+nIndex);
+      }
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      double d;
+      QCBORDecode_GetDoubleConvertAll(&DCtx, 0xffff, &d);
+      if(QCBORDecode_GetError(&DCtx) != pF->uErrorDouble) {
+         return (int32_t)(6000+nIndex);
+      }
+      if(pF->uErrorDouble == QCBOR_SUCCESS) {
+         if(isnan(pF->dConvertToDouble)) {
+            // NaN's can't be compared for equality. A NaN is
+            // never equal to anything including another NaN
+            if(!isnan(d)) {
+               return (int32_t)(7000+nIndex);
+            }
+         } else {
+            if(pF->dConvertToDouble != d) {
+               return (int32_t)(8000+nIndex);
+            }
+         }
+      }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+   }
+
+   return 0;
+}
+
+
+
+
 int32_t CBORSequenceDecodeTests(void)
 {
    QCBORDecodeContext DCtx;
@@ -3832,6 +5730,12 @@
 
    // Get a second item
    uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+   if(uCBORError != QCBOR_ERR_BAD_OPT_TAG) {
+      return 66;
+   }
+
+   // Get a third item
+   uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
    if(uCBORError != QCBOR_SUCCESS) {
       return 2;
    }
@@ -3894,7 +5798,7 @@
    // Try to finish before consuming all bytes to confirm
    // that the still-open error is returned.
    uCBORError = QCBORDecode_Finish(&DCtx);
-   if(uCBORError != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+   if(uCBORError != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED) {
       return 11;
    }
 
@@ -3936,6 +5840,7 @@
 }
 
 
+
 int32_t IntToTests()
 {
    int nErrCode;
@@ -4126,3 +6031,548 @@
    return 0;
 }
 
+
+
+
+/*
+A sequence with
+  A wrapping bstr
+    containing a map
+      1
+      2
+    A wrapping bstr
+       containing an array
+          3
+          wrapping bstr
+             4
+          5
+    6
+  array
+     7
+     8
+ */
+
+static UsefulBufC EncodeBstrWrapTestData(UsefulBuf OutputBuffer)
+{
+   UsefulBufC         Encoded;
+   QCBOREncodeContext EC;
+   QCBORError         uErr;
+
+   QCBOREncode_Init(&EC, OutputBuffer);
+
+   QCBOREncode_BstrWrap(&EC);
+     QCBOREncode_OpenMap(&EC);
+       QCBOREncode_AddInt64ToMapN(&EC, 100, 1);
+       QCBOREncode_AddInt64ToMapN(&EC, 200, 2);
+     QCBOREncode_CloseMap(&EC);
+     QCBOREncode_BstrWrap(&EC);
+       QCBOREncode_OpenArray(&EC);
+         QCBOREncode_AddInt64(&EC, 3);
+         QCBOREncode_BstrWrap(&EC);
+           QCBOREncode_AddInt64(&EC, 4);
+         QCBOREncode_CloseBstrWrap(&EC, NULL);
+         QCBOREncode_AddInt64(&EC, 5);
+       QCBOREncode_CloseArray(&EC);
+     QCBOREncode_CloseBstrWrap(&EC, NULL);
+     QCBOREncode_AddInt64(&EC, 6);
+   QCBOREncode_CloseBstrWrap(&EC, NULL);
+   QCBOREncode_OpenArray(&EC);
+     QCBOREncode_AddInt64(&EC, 7);
+     QCBOREncode_AddInt64(&EC, 8);
+   QCBOREncode_CloseArray(&EC);
+
+   uErr = QCBOREncode_Finish(&EC, &Encoded);
+   if(uErr) {
+      Encoded = NULLUsefulBufC;
+   }
+
+   return Encoded;
+}
+
+
+int32_t EnterBstrTest()
+{
+   MakeUsefulBufOnStack(OutputBuffer, 100);
+
+   QCBORDecodeContext DC;
+
+   QCBORDecode_Init(&DC, EncodeBstrWrapTestData(OutputBuffer), 0);
+
+   int64_t n1, n2, n3, n4, n5, n6, n7, n8;
+
+
+   QCBORDecode_EnterBstrWrapped(&DC, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL);
+     QCBORDecode_EnterMap(&DC, NULL);
+       QCBORDecode_GetInt64InMapN(&DC, 100, &n1);
+       QCBORDecode_GetInt64InMapN(&DC, 200, &n2);
+     QCBORDecode_ExitMap(&DC);
+     QCBORDecode_EnterBstrWrapped(&DC, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL);
+       QCBORDecode_EnterArray(&DC, NULL);
+         QCBORDecode_GetInt64(&DC, &n3);
+         QCBORDecode_EnterBstrWrapped(&DC, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL);
+           QCBORDecode_GetInt64(&DC, &n4);
+         QCBORDecode_ExitBstrWrapped(&DC);
+         QCBORDecode_GetInt64(&DC, &n5);
+       QCBORDecode_ExitArray(&DC);
+     QCBORDecode_ExitBstrWrapped(&DC);
+     QCBORDecode_GetInt64(&DC, &n6);
+   QCBORDecode_ExitBstrWrapped(&DC);
+   QCBORDecode_EnterArray(&DC, NULL);
+     QCBORDecode_GetInt64(&DC, &n7);
+     QCBORDecode_GetInt64(&DC, &n8);
+   QCBORDecode_ExitArray(&DC);
+
+   QCBORError uErr = QCBORDecode_Finish(&DC);
+
+   return (int32_t)uErr;
+}
+
+
+
+
+static const uint8_t spTaggedTypes[] = {
+   0xb2,
+
+      // Date string
+      0x00,
+      0xc0, 0x74, 0x32, 0x30, 0x30, 0x33, 0x2D, 0x31, 0x32, 0x2D,
+      0x31, 0x33, 0x54, 0x31, 0x38, 0x3A, 0x33, 0x30, 0x3A, 0x30,
+      0x32, 0x5A,
+
+      0x01,
+      0x74, 0x32, 0x30, 0x30, 0x33, 0x2D, 0x31, 0x32, 0x2D, 0x31,
+      0x33, 0x54, 0x31, 0x38, 0x3A, 0x33, 0x30, 0x3A, 0x30, 0x32,
+      0x5A,
+
+      // Bignum
+      10,
+      0xC2, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+      0x09, 0x10,
+
+      11,
+      0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+      0x09, 0x10,
+
+      // URL
+      20,
+      0xd8, 0x20, 0x6f, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
+      0x63, 0x62, 0x6F, 0x72, 0x2E, 0x6D, 0x65, 0x2F,
+
+      21,
+      0x6f, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x63, 0x62,
+      0x6F, 0x72, 0x2E, 0x6D, 0x65, 0x2F,
+
+      // B64
+      0x18, 0x1e,
+      0xd8, 0x22, 0x6c, 0x63, 0x47, 0x78, 0x6C, 0x59, 0x58, 0x4E,
+      0x31, 0x63, 0x6D, 0x55, 0x75,
+
+      0x18, 0x1f,
+      0x6c, 0x63, 0x47, 0x78, 0x6C, 0x59, 0x58, 0x4E, 0x31, 0x63,
+      0x6D, 0x55, 0x75,
+
+      // B64URL
+      0x18, 0x28,
+      0xd8, 0x21, 0x6c, 0x63, 0x47, 0x78, 0x6C, 0x59, 0x58, 0x4E,
+      0x31, 0x63, 0x6D, 0x55, 0x75,
+
+      0x18, 0x29,
+      0x6c, 0x63, 0x47, 0x78, 0x6C, 0x59, 0x58, 0x4E, 0x31, 0x63,
+      0x6D, 0x55, 0x75,
+
+      // Regex
+      0x18, 0x32,
+      0xd8, 0x23, 0x68, 0x31, 0x30, 0x30, 0x5C, 0x73, 0x2A, 0x6D,
+      0x6B,
+
+      0x18, 0x33,
+      0x68, 0x31, 0x30, 0x30, 0x5C, 0x73, 0x2A, 0x6D, 0x6B,
+
+      // MIME
+      0x18, 0x3c,
+      0xd8, 0x24, 0x72, 0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65,
+      0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, 0x30,
+      0x0A,
+
+      0x18, 0x3d,
+      0x72, 0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, 0x73,
+      0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, 0x30, 0x0A,
+
+      0x18, 0x3e,
+      0xd9, 0x01, 0x01, 0x52, 0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56,
+      0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
+      0x30, 0x0A,
+
+      0x18, 0x3f,
+      0x52, 0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, 0x73,
+      0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, 0x30, 0x0A,
+
+      // UUID
+      0x18, 0x46,
+      0xd8, 0x25, 0x50, 0x53, 0x4D, 0x41, 0x52, 0x54, 0x43, 0x53,
+      0x4C, 0x54, 0x54, 0x43, 0x46, 0x49, 0x43, 0x41, 0x32,
+
+      0x18, 0x47,
+      0x50, 0x53, 0x4D, 0x41, 0x52, 0x54, 0x43, 0x53, 0x4C, 0x54,
+      0x54, 0x43, 0x46, 0x49, 0x43, 0x41, 0x32
+};
+
+int32_t DecodeTaggedTypeTests()
+{
+   QCBORDecodeContext DC;
+   QCBORError         uErr;
+
+   QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTaggedTypes), 0);
+
+   UsefulBufC String;
+   bool       bNeg;
+
+   QCBORDecode_EnterMap(&DC, NULL);
+   QCBORDecode_GetDateStringInMapN(&DC, 0, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   QCBORDecode_GetDateStringInMapN(&DC, 0, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &String);
+   if(QCBORDecode_GetError(&DC) != QCBOR_SUCCESS) {
+      return 1;
+   }
+   QCBORDecode_GetDateStringInMapN(&DC, 0, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 2;
+   }
+   QCBORDecode_GetDateStringInMapN(&DC, 1, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 3;
+   }
+   QCBORDecode_GetDateStringInMapN(&DC, 1, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &String);
+   QCBORDecode_GetDateStringInMapN(&DC, 1, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 4;
+   }
+   QCBORDecode_GetDateStringInMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 5;
+   }
+
+   QCBORDecode_GetBignumInMapN(&DC, 10, QCBOR_TAG_REQUIREMENT_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS ||
+      bNeg != false) {
+      return 10;
+   }
+   QCBORDecode_GetBignumInMapN(&DC, 11, QCBOR_TAG_REQUIREMENT_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS ||
+      bNeg != true) {
+      return 11;
+   }
+   QCBORDecode_GetBignumInMapN(&DC, 11, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 12;
+   }
+   QCBORDecode_GetBignumInMapN(&DC, 14, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 13;
+   }
+   QCBORDecode_GetBignumInMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 14;
+   }
+
+   QCBORDecode_GetURIInMapN(&DC, 20, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 20;
+   }
+   QCBORDecode_GetURIInMapN(&DC, 21, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 21;
+   }
+   QCBORDecode_GetURIInMapN(&DC, 22, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 22;
+   }
+   QCBORDecode_GetURIInMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 23;
+   }
+
+   QCBORDecode_GetB64InMapN(&DC, 30, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 30;
+   }
+   QCBORDecode_GetB64InMapN(&DC, 31, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 31;
+   }
+   QCBORDecode_GetB64InMapN(&DC, 32, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 32;
+   }
+   QCBORDecode_GetB64InMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 33;
+   }
+
+   QCBORDecode_GetB64URLInMapN(&DC, 40, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 40;
+   }
+   QCBORDecode_GetB64URLInMapN(&DC, 41, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 41;
+   }
+   QCBORDecode_GetB64URLInMapN(&DC, 42, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 42;
+   }
+   QCBORDecode_GetB64URLInMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 43;
+   }
+
+   QCBORDecode_GetRegexInMapN(&DC, 50, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 50;
+   }
+   QCBORDecode_GetRegexInMapN(&DC, 51, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 51;
+   }
+   QCBORDecode_GetRegexInMapN(&DC, 52, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 52;
+   }
+   QCBORDecode_GetRegexInMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 53;
+   }
+
+   // MIME
+   bool bIsNot7Bit;
+   QCBORDecode_GetMIMEMessageInMapN(&DC, 60, QCBOR_TAG_REQUIREMENT_TAG, &String, &bIsNot7Bit);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS ||
+      bIsNot7Bit == true) {
+      return 60;
+   }
+   QCBORDecode_GetMIMEMessageInMapN(&DC, 61, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String, &bIsNot7Bit);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS ||
+      bIsNot7Bit == true) {
+      return 61;
+   }
+   QCBORDecode_GetMIMEMessageInMapN(&DC, 62, QCBOR_TAG_REQUIREMENT_TAG, &String, &bIsNot7Bit);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS ||
+      bIsNot7Bit == false) {
+      return 62;
+   }
+   QCBORDecode_GetMIMEMessageInMapN(&DC, 63, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String, &bIsNot7Bit);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS ||
+      bIsNot7Bit == false) {
+      return 63;
+   }
+   QCBORDecode_GetMIMEMessageInMapN(&DC, 64, QCBOR_TAG_REQUIREMENT_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 64;
+   }
+   QCBORDecode_GetMIMEMessageInMapSZ(&DC, "zzz", QCBOR_TAG_REQUIREMENT_TAG, &String, &bNeg);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 65;
+   }
+
+   QCBORDecode_GetBinaryUUIDInMapN(&DC, 70, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 70;
+   }
+   QCBORDecode_GetBinaryUUIDInMapN(&DC, 71, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_SUCCESS) {
+      return 71;
+   }
+   QCBORDecode_GetBinaryUUIDInMapN(&DC, 72, QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 72;
+   }
+   QCBORDecode_GetBinaryUUIDInMapSZ(&DC, "xxx", QCBOR_TAG_REQUIREMENT_TAG, &String);
+   if(QCBORDecode_GetAndResetError(&DC) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 73;
+   }
+
+   // Improvement: add some more error test cases
+
+   QCBORDecode_ExitMap(&DC);
+
+   uErr = QCBORDecode_Finish(&DC);
+   if(uErr != QCBOR_SUCCESS) {
+      return 100;
+   }
+
+   return 0;
+}
+
+
+
+
+/*
+ [
+    "aaaaaaaaaa",
+    {}
+ ]
+ */
+static const uint8_t spTooLarge1[] = {
+   0x9f,
+   0x6a, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+   0xa0,
+   0xff
+};
+
+/*
+ [
+   {
+      0: "aaaaaaaaaa"
+    }
+ ]
+ */
+static const uint8_t spTooLarge2[] = {
+   0x9f,
+   0xa1,
+   0x00,
+   0x6a, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+   0xff
+};
+
+/*
+ h'A1006A61616161616161616161'
+
+ {
+    0: "aaaaaaaaaa"
+ }
+ */
+static const uint8_t spTooLarge3[] = {
+   0x4d,
+   0xa1,
+   0x00,
+   0x6a, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+};
+
+int32_t TooLargeInputTest(void)
+{
+   QCBORDecodeContext DC;
+   QCBORError         uErr;
+   UsefulBufC         String;
+
+   // These tests require a build with QCBOR_MAX_DECODE_INPUT_SIZE set
+   // to 10 There's not really any way to test this error
+   // condition. The error condition is not complex, so setting
+   // QCBOR_MAX_DECODE_INPUT_SIZE gives an OK test.
+
+   // The input CBOR is only too large because the
+   // QCBOR_MAX_DECODE_INPUT_SIZE is 10.
+   //
+   // This test is disabled for the normal test runs because of the
+   // special build requirement.
+
+
+   // Tests the start of a map being too large
+   QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTooLarge1), QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_EnterArray(&DC, NULL);
+   QCBORDecode_GetTextString(&DC, &String);
+   uErr = QCBORDecode_GetError(&DC);
+   if(uErr != QCBOR_SUCCESS) {
+      return 1;
+   }
+   QCBORDecode_EnterMap(&DC, NULL);
+   uErr = QCBORDecode_GetError(&DC);
+   if(uErr != QCBOR_ERR_INPUT_TOO_LARGE) {
+      return 2;
+   }
+
+   // Tests the end of a map being too large
+   QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTooLarge2), QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_EnterArray(&DC, NULL);
+   QCBORDecode_EnterMap(&DC, NULL);
+   uErr = QCBORDecode_GetError(&DC);
+   if(uErr != QCBOR_SUCCESS) {
+      return 3;
+   }
+   QCBORDecode_ExitMap(&DC);
+   uErr = QCBORDecode_GetError(&DC);
+   if(uErr != QCBOR_ERR_INPUT_TOO_LARGE) {
+      return 4;
+   }
+
+   // Tests the entire input CBOR being too large when processing bstr wrapping
+   QCBORDecode_Init(&DC, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTooLarge3), QCBOR_DECODE_MODE_NORMAL);
+   QCBORDecode_EnterBstrWrapped(&DC, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL);
+   uErr = QCBORDecode_GetError(&DC);
+   if(uErr != QCBOR_ERR_INPUT_TOO_LARGE) {
+      return 5;
+   }
+
+   return 0;
+}
+
+
+static const uint8_t spMapWithIndefLenStrings[] = {
+   0xbf,
+      0x7f, 0x61, 'l', 0x64, 'a', 'b', 'e', 'l' , 0x61, '1', 0xff,
+      0x5f, 0x42, 0x01, 0x02, 0x43, 0x03, 0x04, 0x05, 0xff,
+      0x7f, 0x62, 'd', 'y', 0x61, 'm', 0x61, 'o', 0xff,
+      0x03,
+      0x7f, 0x62, 'l', 'a', 0x63, 'b', 'e', 'l', 0x61, '2', 0xff,
+      0xc3,
+          0x5f, 0x42, 0x00, 0x01, 0x42, 0x00, 0x01, 0x41, 0x01, 0xff,
+   0xff
+};
+
+int32_t SpiffyIndefiniteLengthStringsTests()
+{
+   QCBORDecodeContext DCtx;
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spMapWithIndefLenStrings),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   MakeUsefulBufOnStack(StringBuf, 200);
+   QCBORDecode_SetMemPool(&DCtx, StringBuf, false);
+
+   UsefulBufC ByteString;
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetByteStringInMapSZ(&DCtx, "label1", &ByteString);
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 1;
+   }
+
+   const uint8_t pExectedBytes[] = {0x01, 0x02, 0x03, 0x04, 0x05};
+   if(UsefulBuf_Compare(ByteString, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExectedBytes))) {
+      return 2;
+   }
+
+   uint64_t uInt;
+   QCBORDecode_GetUInt64InMapSZ(&DCtx, "dymo", &uInt);
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 3;
+   }
+   if(uInt != 3) {
+      return 4;
+   }
+
+   double uDouble;
+   QCBORDecode_GetDoubleConvertAllInMapSZ(&DCtx,
+                                          "label2",
+                                          0xff,
+                                          &uDouble);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 5;
+   }
+   if(uDouble != -16777474) {
+      return 6;
+   }
+#else
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_HW_FLOAT_DISABLED) {
+      return 7;
+   }
+#endif
+
+
+   QCBORDecode_ExitMap(&DCtx);
+
+   if(QCBORDecode_Finish(&DCtx)) {
+      return 99;
+   }
+
+   return 0;
+}
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 26752e0..3cbb82d 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -161,13 +161,19 @@
 
 
 /*
- Thest the date types -- epoch and strings
+ Test the date types -- epoch and strings
  */
 int32_t DateParseTest(void);
 
 
 /*
-  Test optional tags like the CBOR magic number.
+ Test spiffy date decoding functions
+ */
+int32_t SpiffyDateDecodeTest(void);
+
+
+/*
+  Test decode of CBOR tagging like the CBOR magic number and many others.
  */
 int32_t OptTagParseTest(void);
 
@@ -252,6 +258,9 @@
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 
 
+int32_t EnterMapTest(void);
+
+int32_t IntegerConvertTest(void);
 /*
  Tests decoding of CBOR Sequences defined in RFC 8742
  */
@@ -263,4 +272,30 @@
 */
 int32_t IntToTests(void);
 
+
+/*
+ Test the decoding of bstr-wrapped CBOR.
+ */
+int32_t EnterBstrTest(void);
+
+
+/*
+ Test decoding of tagged types like UUID
+ */
+int32_t DecodeTaggedTypeTests(void);
+
+
+/*
+ Test the detection of input that is too large. Requires
+ a special build that makes QCBOR_MAX_DECODE_INPUT_SIZE small.
+ */
+int32_t TooLargeInputTest(void);
+
+
+/*
+ Test spiffy decoding of indefinite length strings.
+ */
+int32_t SpiffyIndefiniteLengthStringsTests(void);
+
+
 #endif /* defined(__QCBOR__qcbort_decode_tests__) */
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 1e91378..c573a4b 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -316,8 +316,8 @@
  0x1c, 0x59, 0x57, 0x35, 0x35, 0x49, 0x47, 0x4e, 0x68, 0x63,
  0x6d, 0x35, 0x68, 0x62, 0x43, 0x42, 0x77, 0x62, 0x47, 0x56,
  0x68, 0x63, 0x33, 0x56, 0x79, 0x5a, 0x51, 0x3d, 0x3d, 0xd8,
- 0x23, 0x67, 0x5b, 0x5e, 0x61, 0x62, 0x63, 0x5d, 0x2b, 0xd8,
- 0x24, 0x79, 0x01, 0x57, 0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56,
+ 0x23, 0x67, 0x5b, 0x5e, 0x61, 0x62, 0x63, 0x5d, 0x2b, 0xd9,
+ 0x01, 0x01, 0x59, 0x01, 0x57, 0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56,
  0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e,
  0x30, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
  0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x6d, 0x75, 0x6c, 0x74,
@@ -376,7 +376,7 @@
  0x79, 0x5a, 0x53, 0x34, 0x3d, 0x64, 0x70, 0x6f, 0x70, 0x6f,
  0xd8, 0x23, 0x68, 0x31, 0x30, 0x30, 0x5c, 0x73, 0x2a, 0x6d,
  0x6b, 0x38, 0x32, 0xd8, 0x23, 0x66, 0x70, 0x65, 0x72, 0x6c,
- 0x5c, 0x42, 0x63, 0x4e, 0x65, 0x64, 0xd8, 0x24, 0x79, 0x01,
+ 0x5c, 0x42, 0x63, 0x4e, 0x65, 0x64, 0xd9, 0x01, 0x01, 0x59, 0x01,
  0x57, 0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56, 0x65, 0x72, 0x73,
  0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x0a, 0x43,
  0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70,
@@ -411,7 +411,7 @@
  0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x65, 0x78,
  0x74, 0x0a, 0x0a, 0x2d, 0x2d, 0x58, 0x58, 0x58, 0x58, 0x62,
  0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x20, 0x74, 0x65,
- 0x78, 0x74, 0x2d, 0x2d, 0x0a, 0xd8, 0x24, 0x79, 0x01, 0x57,
+ 0x78, 0x74, 0x2d, 0x2d, 0x0a, 0xd9, 0x01, 0x01, 0x59, 0x01, 0x57,
  0x4d, 0x49, 0x4d, 0x45, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69,
  0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x0a, 0x43, 0x6f,
  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65,
@@ -514,7 +514,7 @@
 
 int32_t AllAddMethodsTest()
 {
-   // TODO: this test should be broken down into several so it is more
+   // Improvement: this test should be broken down into several so it is more
    // managable. Tags and labels could be more sensible
    QCBOREncodeContext ECtx;
    int nReturn = 0;
@@ -1950,7 +1950,7 @@
       return nReturn;
    }
    if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
-      return -1; // TODO: better type?
+      return QCBOR_ERR_UNEXPECTED_TYPE;
    }
 
    *pBstr = Item.val.string;
@@ -2486,14 +2486,14 @@
    QCBOREncode_Init(&EC, Large);
    QCBOREncode_OpenArray(&EC);
    QCBOREncode_AddSimple(&EC, 24); // CBOR_SIMPLEV_RESERVED_START
-   if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_UNSUPPORTED) {
+   if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_ENCODE_UNSUPPORTED) {
       return -12;
    }
 
    QCBOREncode_Init(&EC, Large);
    QCBOREncode_OpenArray(&EC);
    QCBOREncode_AddSimple(&EC, 31); // CBOR_SIMPLEV_RESERVED_END
-   if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_UNSUPPORTED) {
+   if(QCBOREncode_FinishGetSize(&EC, &xx) != QCBOR_ERR_ENCODE_UNSUPPORTED) {
       return -13;
    }
 
@@ -2628,7 +2628,7 @@
 
    QCBOREncode_AddDecimalFractionToMapN(&EC, 300, 3, -1);
 
-   QCBOREncode_AddDecimalFractionBigNumToMap(&EC,
+   QCBOREncode_AddDecimalFractionBigNumToMapSZ(&EC,
                                              "decimal fraction bignum postive",
                                              BigNum,
                                              false,
@@ -2640,7 +2640,7 @@
                                               false,
                                               INT32_MAX);
 
-   QCBOREncode_AddDecimalFractionBigNumToMap(&EC,
+   QCBOREncode_AddDecimalFractionBigNumToMapSZ(&EC,
                                              "decimal fraction bignum negative",
                                              BigNum,
                                              true,
diff --git a/test/run_tests.c b/test/run_tests.c
index e4b2713..0609736 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -56,6 +56,9 @@
 
 
 static test_entry s_tests[] = {
+    TEST_ENTRY(EnterBstrTest),
+    TEST_ENTRY(IntegerConvertTest),
+    TEST_ENTRY(EnterMapTest),
     TEST_ENTRY(QCBORHeadTest),
     TEST_ENTRY(EmptyMapsAndArraysTest),
     TEST_ENTRY(NotWellFormedTests),
@@ -84,6 +87,7 @@
     TEST_ENTRY(BignumParseTest),
     TEST_ENTRY(OptTagParseTest),
     TEST_ENTRY(DateParseTest),
+    TEST_ENTRY(SpiffyDateDecodeTest),
     TEST_ENTRY(ShortBufferParseTest2),
     TEST_ENTRY(ShortBufferParseTest),
     TEST_ENTRY(ParseDeepArrayTest),
@@ -104,17 +108,20 @@
     TEST_ENTRY(CoseSign1TBSTest),
     TEST_ENTRY(StringDecoderModeFailTest),
     TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
+    TEST_ENTRY_DISABLED(TooLargeInputTest),
     TEST_ENTRY(EncodeErrorTests),
     TEST_ENTRY(SetUpAllocatorTest),
     TEST_ENTRY(SimpleValuesIndefiniteLengthTest1),
     TEST_ENTRY(EncodeLengthThirtyoneTest),
     TEST_ENTRY(CBORSequenceDecodeTests),
     TEST_ENTRY(IntToTests),
+    TEST_ENTRY(DecodeTaggedTypeTests),
 #ifndef     QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
     TEST_ENTRY(EncodeLengthThirtyoneTest),
     TEST_ENTRY(ExponentAndMantissaDecodeTests),
     TEST_ENTRY(ExponentAndMantissaDecodeFailTests),
     TEST_ENTRY(ExponentAndMantissaEncodeTests),
+    TEST_ENTRY(SpiffyIndefiniteLengthStringsTests),
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 };
 
@@ -284,6 +291,7 @@
 // For size printing
 #include "qcbor/qcbor_encode.h"
 #include "qcbor/qcbor_decode.h"
+#include "qcbor/qcbor_spiffy_decode.h"
 
 
 /*
@@ -316,5 +324,6 @@
    PrintSize("sizeof(QCBORItem)",           (uint32_t)sizeof(QCBORItem),          pfOutput, pOutCtx);
    PrintSize("sizeof(QCBORTagListIn)",      (uint32_t)sizeof(QCBORTagListIn),     pfOutput, pOutCtx);
    PrintSize("sizeof(QCBORTagListOut)",     (uint32_t)sizeof(QCBORTagListOut),    pfOutput, pOutCtx);
+   PrintSize("sizeof(TagSpecification)",    (uint32_t)sizeof(TagSpecification),    pfOutput, pOutCtx);
    (*pfOutput)("", pOutCtx, 1);
 }