Merge branch 'entermap' into AdvancedDecode
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index c2dddae..e12ca0e 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -29,6 +29,8 @@
 		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 */; };
+		E788315D243C02EA001893CD /* qcbor_decode_map.c in Sources */ = {isa = PBXBuildFile; fileRef = E788315C243C02EA001893CD /* qcbor_decode_map.c */; };
+		E788315E243C0669001893CD /* qcbor_decode_map.c in Sources */ = {isa = PBXBuildFile; fileRef = E788315C243C02EA001893CD /* qcbor_decode_map.c */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -77,9 +79,11 @@
 		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>"; };
+		E788315B243BFDF0001893CD /* qcbor_decode_map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qcbor_decode_map.h; path = inc/qcbor/qcbor_decode_map.h; sourceTree = "<group>"; };
+		E788315C243C02EA001893CD /* qcbor_decode_map.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = qcbor_decode_map.c; 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>"; };
+		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; lastKnownFileType = sourcecode.c.h; name = qcbor_encode.h; path = inc/qcbor/qcbor_encode.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -130,6 +134,7 @@
 				E776E08D214AE07500E67947 /* UsefulBuf.c */,
 				E73B57582161CA690080D658 /* ieee754.c */,
 				E73B57572161CA680080D658 /* ieee754.h */,
+				E788315C243C02EA001893CD /* qcbor_decode_map.c */,
 			);
 			name = src;
 			sourceTree = "<group>";
@@ -143,6 +148,7 @@
 				E78C91E0240C90C100F4CECE /* qcbor_private.h */,
 				E776E093214AE08B00E67947 /* qcbor.h */,
 				E776E094214AE09700E67947 /* UsefulBuf.h */,
+				E788315B243BFDF0001893CD /* qcbor_decode_map.h */,
 			);
 			name = inc;
 			sourceTree = "<group>";
@@ -248,6 +254,7 @@
 				E772021A23B52C02006E966E /* run_tests.c in Sources */,
 				E772021B23B52C02006E966E /* qcbor_decode.c in Sources */,
 				E772021C23B52C02006E966E /* float_tests.c in Sources */,
+				E788315E243C0669001893CD /* qcbor_decode_map.c in Sources */,
 				E772021D23B52C02006E966E /* qcbor_decode_tests.c in Sources */,
 				E772021E23B52C02006E966E /* UsefulBuf.c in Sources */,
 				E772021F23B52C02006E966E /* qcbor_encode_tests.c in Sources */,
@@ -266,6 +273,7 @@
 				E73B57652161F8F80080D658 /* run_tests.c in Sources */,
 				E776E091214AE07500E67947 /* qcbor_decode.c in Sources */,
 				E73B575E2161CA7C0080D658 /* float_tests.c in Sources */,
+				E788315D243C02EA001893CD /* qcbor_decode_map.c in Sources */,
 				0FA9BEB7216CE6CA00BA646B /* qcbor_decode_tests.c in Sources */,
 				E776E090214AE07500E67947 /* UsefulBuf.c in Sources */,
 				0FA9BEBA216DC7AD00BA646B /* qcbor_encode_tests.c in Sources */,
@@ -361,6 +369,7 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_LABEL = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = inc;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -420,6 +429,7 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_LABEL = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = inc;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = macosx;
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index aaea610..d7d7d4a 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -319,7 +319,16 @@
     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
+    QCBOR_ERR_STRING_TOO_LONG = 24,
+    
+    /** The type decoded was not was  expected */
+    QCBOR_ERR_UNEXPECTED_TYPE = 25,
+    
+    /** Duplicate label in map detected */
+    QCBOR_ERR_DUPLICATE_LABEL = 26,
+    
+    /** Item with requested label is not found */
+    QCBOR_ERR_NOT_FOUND = 27
 
     /* This is stored in uint8_t in places; never add values > 255 */
 } QCBORError;
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 9499e7f..1d788b1 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -49,6 +49,91 @@
 
 
 /**
+@file qcbor_decode.h
+
+Q C B O R    D e c o d e
+
+ This section just discusses decoding assuming familiarity with the general
+ description of this encoder / decoder in section XXX.
+ 
+ Encoded CBOR can be viewed to have a tree structure
+ where the lead 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
+ 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 EnterMap and GetByLabel methods,
+ map items can be accessed by label. EnterMap
+narrows decoding to a particular map. GetByLabel
+ allows decoding the item of a particular label in
+ the particular map. This can be used with nested
+ maps by calling EnterMapByLabel.
+ 
+ When EnterMap is called, pre-order traversal
+ continues to work. There is a cursor that is run
+ over the tree with calls to GetNext. This can be
+ intermixed with calls to GetByLabel. The pre-order
+ traversal is limited just to the map entered. Attempts
+ to GetNext beyond the end of the map will give
+ the HIT END error.
+ 
+  There is also EnterArray to decode arrays. It will
+ narrow the traversal to the extent of the array
+ entered.
+ 
+ GetByLabel supports duplicate label detection
+ and will result in an error if the map has
+ duplicate labels.
+ 
+ GetByLabel is 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 GetItems which allows decoding of
+ a list of items expected in an map in one
+ traveral.
+ 
+ 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. The only error check needed
+ is in the Finish call. 
+ 
+ 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.
+  
+ In some CBOR protocols, the type of
+ a data item may be variable. Maybe even
+ the type of one data item is dependent
+ on another. In such designs, GetNext has
+ to be used and the internal error checking
+ can't be relied upon.
+ 
+ 
+
+*/
+
+/**
  The decode mode options.
  */
 typedef enum {
@@ -824,6 +909,220 @@
 
 
 
+
+
+
+//
+//  qcbor_decode_map.h
+//  QCBOR
+//
+//  Created by Laurence Lundblade on 4/6/20.
+//  Copyright © 2020 Laurence Lundblade. All rights reserved.
+//
+
+#ifndef qcbor_decode_map_h
+#define qcbor_decode_map_h
+
+
+#include "qcbor_decode.h"
+
+
+
+
+/* Next item must be map or this generates an error.
+ 
+ 
+This puts the decoder in map mode which narrows
+decoding to the map entered and enables use of
+getting items by label.
+ 
+ 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 map mode is fully exited.
+ 
+ While in map mode, GetNext works as usual on the
+ map and the standard in-order traversal cursor
+ is maintained. Attempts to get items off the end of the
+ map will give error XXX (rather going to the next
+ item after the map as it would when not in map
+ mode).
+ 
+ You can rewind the inorder traversal cursor to the
+ beginning of the map with RewindMap().
+ 
+ Exiting leaves the cursor at the
+ data item following the last entry in the map.
+ 
+ Entering and Exiting map mode consumes the whole
+ map and its contents as a GetNext after exiting
+ will return the item after the map. */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pCtx);
+
+/*
+ Indicate if decoding is in map mode more not.
+ */
+bool QCBORDecode_InMapMode(QCBORDecodeContext *pCtxt);
+
+
+/*
+ Restarts fetching of items in a map to the start of the
+ map. This is for GetNext. It has no effect on
+ GetByLabel (which always searches from the start).
+ */
+void QCBORDecode_RewindMap(QCBORDecodeContext *pCtxt);
+
+
+QCBORError QCBORDecode_EnterArray(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitArray(QCBORDecodeContext *pCtx);
+
+QCBORError QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel);
+
+
+//QCBORError QCBORDecode_EnterMapX(QCBORDecodeContext *pCtx,  MapDecode *pMap);
+
+                     
+
+
+/*
+ Get an item out of a map.
+ 
+ Decoding must be in map mode for this to work.
+ 
+ 
+ 
+Seek to the beginning of the map.
+Consume items looking for the nLabel.
+Always go through the whole map and always look for duplicates.
+Return the item found, if no errors.
+
+Allow specification of type required.
+
+
+
+*/
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pCtx,
+                         int64_t nLabel,
+                         uint8_t qcbor_type,
+                         QCBORItem *pItem);
+
+
+QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
+const char *szLabel,
+uint8_t qcbor_type,
+QCBORItem *pItem);
+
+/*
+ This gets several labeled items out of a map.
+ 
+ pItemArray is an array of items terminated by an item
+ with uLabelType QCBOR_TYPE_NONE.
+ 
+ On input the the array of items is the list of labels to fetch
+ items for.
+ 
+ On output the array is the data items found. If the label
+ wasn't found, uDataType is QCBOR_TYPE_NONE.
+ 
+ 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.
+ 
+ If any duplicate labels are detected, this returns an error.
+ 
+ This will return maps and arrays that are in the map, but
+ provides no way to descend into and decode them.
+ 
+ */
+QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList);
+
+
+
+QCBORError QCBORDecode_GetIntInMap(QCBORDecodeContext *pCtx, int64_t nLabel, int64_t *pInt);
+
+void QCBORDecode_GetIntInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, int64_t *pInt);
+
+
+void QCBORDecode_GetBstrInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+void QCBORDecode_GetTextInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+
+/*
+  Find a map in a map by integer label and enter it.
+ 
+ This will do duplicate detection on the particular label.
+ 
+ Call QCBORDecode_ExitMap() to return to the mode / level
+ from before this was called.
+ 
+ Seek to to the beginning of the map.
+ Consume items looking for nLabel
+ */
+QCBORError QCBORDecode_EnterMapFromMap(QCBORDecodeContext *pCtx, int64_t nLabel);
+
+QCBORError QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pCtx, const char  *szLabel);
+
+
+
+
+/*
+ Normally decoding is just in-order traversal. You can get next
+ of any type, get next of a particular type including conversions.
+ 
+ If the cursor is at a map and you enter it, then you can use
+ methods that Get things by label, either numeric or string.
+ 
+ These methods work only at the particular level in the map.
+ To go into a map nested in a map call the special method
+ to enter a map by label.
+ 
+ When in a map, the GetNext methods work too, but only
+ to the end of the map. You can't traverse off the end of the
+ map.
+ 
+ You can rewind to the start of the map and traverse it again
+ with the MapRestart method.
+ 
+ The exit map method will leave the traversal cursor at the first itme after
+ the map.
+ 
+ 
+  The beginning of each map must be recorded so the scan can be done
+ through the whole map.
+ 
+  A bit per level to indicate in map mode for that level so
+  it is clear what GetNext at end does and what happens on MapExit
+ and where to set the cursor.
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ */
+
+
+
+
+
+#endif /* qcbor_decode_map_h */
+
+
+
+
 /**
  @brief Convert int64_t to smaller integers safely.
 
diff --git a/inc/qcbor/qcbor_decode_map.h b/inc/qcbor/qcbor_decode_map.h
new file mode 100644
index 0000000..ec6c43b
--- /dev/null
+++ b/inc/qcbor/qcbor_decode_map.h
@@ -0,0 +1,209 @@
+#if 0
+//
+//  qcbor_decode_map.h
+//  QCBOR
+//
+//  Created by Laurence Lundblade on 4/6/20.
+//  Copyright © 2020 Laurence Lundblade. All rights reserved.
+//
+
+#ifndef qcbor_decode_map_h
+#define qcbor_decode_map_h
+
+
+#include "qcbor_decode.h"
+
+
+
+
+/* Next item must be map or this generates an error.
+ 
+ 
+This puts the decoder in map mode which narrows
+decoding to the map entered and enables use of
+getting items by label.
+ 
+ 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 map mode is fully exited.
+ 
+ While in map mode, GetNext works as usual on the
+ map and the standard in-order traversal cursor
+ is maintained. Attempts to get items off the end of the
+ map will give error XXX (rather going to the next
+ item after the map as it would when not in map
+ mode).
+ 
+ You can rewind the inorder traversal cursor to the
+ beginning of the map with RewindMap().
+ 
+ Exiting leaves the cursor at the
+ data item following the last entry in the map.
+ 
+ Entering and Exiting map mode consumes the whole
+ map and its contents as a GetNext after exiting
+ will return the item after the map. */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pCtx);
+
+/*
+ Indicate if decoding is in map mode more not.
+ */
+bool QCBORDecode_InMapMode(QCBORDecodeContext *pCtxt);
+
+
+/*
+ Restarts fetching of items in a map to the start of the
+ map. This is for GetNext. It has no effect on
+ GetByLabel (which always searches from the start).
+ */
+void QCBORDecode_RewindMap(QCBORDecodeContext *pCtxt);
+
+
+QCBORError QCBORDecode_EnterArray(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitArray(QCBORDecodeContext *pCtx);
+
+QCBORError QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel);
+
+
+//QCBORError QCBORDecode_EnterMapX(QCBORDecodeContext *pCtx,  MapDecode *pMap);
+
+                     
+
+
+/*
+ Get an item out of a map.
+ 
+ Decoding must be in map mode for this to work.
+ 
+ 
+ 
+Seek to the beginning of the map.
+Consume items looking for the nLabel.
+Always go through the whole map and always look for duplicates.
+Return the item found, if no errors.
+
+Allow specification of type required.
+
+
+
+*/
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pCtx,
+                         int64_t nLabel,
+                         uint8_t qcbor_type,
+                         QCBORItem *pItem);
+
+
+QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
+const char *szLabel,
+uint8_t qcbor_type,
+QCBORItem *pItem);
+
+/*
+ This gets several labeled items out of a map.
+ 
+ pItemArray is an array of items terminated by an item
+ with uLabelType QCBOR_TYPE_NONE.
+ 
+ On input the the array of items is the list of labels to fetch
+ items for.
+ 
+ On output the array is the data items found. If the label
+ wasn't found, uDataType is QCBOR_TYPE_NONE.
+ 
+ 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.
+ 
+ If any duplicate labels are detected, this returns an error.
+ 
+ This will return maps and arrays that are in the map, but
+ provides no way to descend into and decode them.
+ 
+ */
+QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList);
+
+
+
+QCBORError QCBORDecode_GetIntInMap(QCBORDecodeContext *pCtx, int64_t nLabel, int64_t *pInt);
+
+QCBORError QCBORDecode_GetIntInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, int64_t *pInt);
+
+
+void QCBORDecode_GetBstrInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+void QCBORDecode_GetTextInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+
+/*
+  Find a map in a map by integer label and enter it.
+ 
+ This will do duplicate detection on the particular label.
+ 
+ Call QCBORDecode_ExitMap() to return to the mode / level
+ from before this was called.
+ 
+ Seek to to the beginning of the map.
+ Consume items looking for nLabel
+ */
+QCBORError QCBORDecode_EnterMapFromMap(QCBORDecodeContext *pCtx, int64_t nLabel);
+
+QCBORError QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pCtx, const char  *szLabel);
+
+
+
+
+/*
+ Normally decoding is just in-order traversal. You can get next
+ of any type, get next of a particular type including conversions.
+ 
+ If the cursor is at a map and you enter it, then you can use
+ methods that Get things by label, either numeric or string.
+ 
+ These methods work only at the particular level in the map.
+ To go into a map nested in a map call the special method
+ to enter a map by label.
+ 
+ When in a map, the GetNext methods work too, but only
+ to the end of the map. You can't traverse off the end of the
+ map.
+ 
+ You can rewind to the start of the map and traverse it again
+ with the MapRestart method.
+ 
+ The exit map method will leave the traversal cursor at the first itme after
+ the map.
+ 
+ 
+  The beginning of each map must be recorded so the scan can be done
+ through the whole map.
+ 
+  A bit per level to indicate in map mode for that level so
+  it is clear what GetNext at end does and what happens on MapExit
+ and where to set the cursor.
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ */
+
+
+
+
+
+#endif /* qcbor_decode_map_h */
+#endif
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index a7cb440..17ee20e 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -123,9 +123,12 @@
  */
 typedef struct __QCBORDecodeNesting  {
   // PRIVATE DATA STRUCTURE
-   struct {
-      uint16_t uCount;
-      uint8_t  uMajorType;
+   struct nesting_decode_level {
+      uint32_t uOffset;
+      uint16_t uCount; // Cursor
+      uint8_t  uMajorType; // TODO: one bit?
+      uint8_t  uMapMode; // Used by map mode TODO: one bit?
+      uint16_t uSaveCount; // Used by map mode
    } pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING1+1],
    *pCurrent;
 } QCBORDecodeNesting;
@@ -154,8 +157,13 @@
 
    uint8_t        uDecodeMode;
    uint8_t        bStringAllocateAll;
+   uint8_t        uLastError;  // QCBORError stuffed into a uint8_t
 
    QCBORDecodeNesting nesting;
+   
+   // A cached offset to the end of the current map
+   // 0 if no value is cached.
+   uint32_t uMapEndOffset;
 
    // If a string allocator is configured for indefinite-length
    // strings, it is configured here.
diff --git a/qcbor_decode_map.c b/qcbor_decode_map.c
new file mode 100644
index 0000000..9bc7dc3
--- /dev/null
+++ b/qcbor_decode_map.c
@@ -0,0 +1,9 @@
+//
+//  qcbor_decode_map.c
+//  QCBOR
+//
+//  Created by Laurence Lundblade on 4/6/20.
+//  Copyright © 2020 Laurence Lundblade. All rights reserved.
+//
+
+#include <stdio.h>
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index d3d1ace..619da9a 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -62,6 +62,27 @@
    return pNesting->pCurrent != &(pNesting->pMapsAndArrays[0]);
 }
 
+inline static bool
+DecodeNesting_AtEnd(const QCBORDecodeNesting *pNesting)
+{
+   if(!DecodeNesting_IsNested(pNesting)){
+      // Always at end if at the top level of nesting
+      return true;
+   }
+   
+   if(!pNesting->pCurrent->uMapMode) {
+      // If not in map mode then it is as IsNested says
+      return false;
+   }
+   
+   // In map mode at the current nesting level. In this
+   // mode we are at the end of a pre-order traversal
+   // if the count is zero
+   // TODO: what about indefinite length maps & arrays?
+   return pNesting->pCurrent->uCount == 0;
+}
+
+
 inline static int
 DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
 {
@@ -72,7 +93,7 @@
 DecodeNesting_GetLevel(QCBORDecodeNesting *pNesting)
 {
    // Check in DecodeNesting_Descend and never having
-   // QCBOR_MAX_ARRAY_NESTING > 255 gaurantee cast is safe
+   // QCBOR_MAX_ARRAY_NESTING > 255 gaurantees cast is safe
    return (uint8_t)(pNesting->pCurrent - &(pNesting->pMapsAndArrays[0]));
 }
 
@@ -106,11 +127,11 @@
    return QCBOR_SUCCESS;
 }
 
-// Called on every single item except breaks including open of a map/array
+// Called on every single item except breaks including decode of a map/array
 inline static void
 DecodeNesting_DecrementCount(QCBORDecodeNesting *pNesting)
 {
-   while(DecodeNesting_IsNested(pNesting)) {
+   while(!DecodeNesting_AtEnd(pNesting)) {
       // Not at the top level, so there is decrementing to be done.
 
       if(!DecodeNesting_IsIndefiniteLength(pNesting)) {
@@ -122,6 +143,11 @@
          // Did not close out an array or map, so nothing further
          break;
       }
+      
+      if(pNesting->pCurrent->uMapMode) {
+         // In map mode the level-up must be done explicitly
+         break;
+      }
 
       // Closed out an array or map so level up
       pNesting->pCurrent--;
@@ -1034,6 +1060,7 @@
 
 /*
  Public function, see header qcbor/qcbor_decode.h file
+ TODO: correct this comment
  */
 QCBORError QCBORDecode_GetNextMapOrArray(QCBORDecodeContext *me,
                                          QCBORItem *pDecodedItem,
@@ -1041,15 +1068,19 @@
 {
    // 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
+   // Check if there are an TODO: incomplete comment
    if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) == 0 && !DecodeNesting_IsNested(&(me->nesting))) {
       nReturn = QCBOR_ERR_NO_MORE_ITEMS;
       goto Done;
    }
+   
+   // This is to handle map and array mode
+   if(UsefulInputBuf_Tell(&(me->InBuf)) != 0 && DecodeNesting_AtEnd(&(me->nesting))) {
+      nReturn = QCBOR_ERR_NO_MORE_ITEMS;
+      goto Done;
+   }
 
    nReturn = GetNext_MapEntry(me, pDecodedItem, pTags);
    if(nReturn) {
@@ -1427,7 +1458,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.
@@ -1718,3 +1749,531 @@
 
    return QCBOR_SUCCESS;
 }
+
+#include <stdio.h>
+void printdecode(QCBORDecodeContext *pMe, const char *szName)
+{
+   printf("---%s---\nLevel   Count   Type   Offset  SaveCount\n", szName);
+   for(int i = 0; i < 4 /*QCBOR_MAX_ARRAY_NESTING*/; i++) {
+      const char *szX = (i == DecodeNesting_GetLevel(&(pMe->nesting))) ? "->" : "  ";
+      printf("%s %2d   %5d     %2d   %6u         %2d\n",
+             szX,
+             i,
+             pMe->nesting.pMapsAndArrays[i].uCount,
+             pMe->nesting.pMapsAndArrays[i].uMajorType,
+             pMe->nesting.pMapsAndArrays[i].uOffset,
+             pMe->nesting.pMapsAndArrays[i].uSaveCount
+             );
+   }
+   printf("\n");
+}
+
+
+/*
+ * Public function. See qcbor_util.h
+ */
+static QCBORError
+ConsumeItem(QCBORDecodeContext *pMe,
+            const QCBORItem    *pItemToConsume,
+            uint_fast8_t       *puNextNestLevel)
+{
+   QCBORError nReturn;
+   QCBORItem  Item;
+   
+   printdecode(pMe, "ConsumeItem");
+
+   if(pItemToConsume->uDataType == QCBOR_TYPE_MAP ||
+      pItemToConsume->uDataType == QCBOR_TYPE_ARRAY) {
+      /* There is only real work to do for maps and arrays */
+
+      /* This works for definite and indefinite length
+       * maps and arrays by using the nesting level
+       */
+      do {
+         nReturn = QCBORDecode_GetNext(pMe, &Item);
+         if(nReturn != QCBOR_SUCCESS) {
+            goto Done;
+         }
+      } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
+
+      if(puNextNestLevel != NULL) {
+         *puNextNestLevel = Item.uNextNestLevel;
+      }
+      nReturn = QCBOR_SUCCESS;
+
+   } else {
+     /* item_to_consume is not a map or array */
+      if(puNextNestLevel != NULL) {
+         /* Just pass the nesting level through */
+         *puNextNestLevel = pItemToConsume->uNextNestLevel;
+      }
+      nReturn = QCBOR_SUCCESS;
+   }
+
+Done:
+    return nReturn;
+}
+
+
+/* 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;
+      }
+   }
+   /* Other label types are never matched */
+   return false;
+}
+
+/*
+ * Public function. qcbor_util.h
+ */
+QCBORError
+GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemArray, size_t *puOffset)
+{
+   QCBORItem  *pIterator;
+   QCBORError  nReturn;
+   
+   
+
+   printdecode(pMe, "GetItemsInMapStart");
+
+
+   // TODO: check we are in map mode
+
+   /* Clear structure holding the items found */
+   for(pIterator = pItemArray; pIterator->uLabelType != 0; pIterator++) {
+      pIterator->uDataType = QCBOR_TYPE_NONE;
+   }
+
+   // Save the cursor and such used for pre-order traversal
+/*   const size_t   uSave        = UsefulInputBuf_Tell(&(pMe->InBuf));
+   const uint16_t uSaveCount   = pMe->nesting.pCurrent->uCount;
+   struct nesting_decode_level *pSaveCurrent = pMe->nesting.pCurrent;
+*/
+   QCBORDecodeNesting N = pMe->nesting;
+   
+   if(pMe->nesting.pCurrent->uCount != UINT16_MAX) {
+      pMe->nesting.pCurrent->uCount = pMe->nesting.pCurrent->uSaveCount;
+   }
+
+   UsefulInputBuf_Seek(&(pMe->InBuf), pMe->nesting.pCurrent->uOffset);
+
+   /* Loop over all the items in the map. They could be
+   * deeply nested and this should handle both definite
+   * and indefinite length maps and arrays, so this
+   * adds some complexity. */
+   const uint8_t uMapNestLevel = DecodeNesting_GetLevel(&(pMe->nesting));
+
+   while(1) {
+      QCBORItem Item;
+        
+      const size_t uOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+      printdecode(pMe, "GetItemsInMapMid1");
+
+      if((nReturn = QCBORDecode_GetNext(pMe, &Item)) != QCBOR_SUCCESS) {
+         /* Got non-well-formed CBOR */
+         goto Done;
+      }
+      
+      printdecode(pMe, "GetItemsInMapMid2");
+
+       
+      // Loop over all the items to check this item against
+      for(pIterator = pItemArray; pIterator->uLabelType != 0; pIterator++) {
+         if(MatchLabel(Item, *pIterator)) {
+            // A label match has been found
+            if(pIterator->uDataType != QCBOR_TYPE_NONE) {
+               nReturn = QCBOR_ERR_DUPLICATE_LABEL;
+               goto Done;
+            }
+            
+            /* Successful match. Return the item. */
+            *pIterator = Item;
+            if(puOffset) {
+               *puOffset = uOffset;
+            }
+         }
+      }
+         
+       /* Still have to consume the item that did or didn't match.
+          The item could be a deeply nested array or map. */
+
+        /* Only looking at top-level data items, so just consume any
+         * map or array encountered.*/
+      uint_fast8_t uNextNestLevel;
+
+      nReturn = ConsumeItem(pMe, &Item, &uNextNestLevel);
+      if(nReturn) {
+         goto Done;
+      }
+      if(uNextNestLevel < uMapNestLevel) {
+         nReturn = QCBOR_SUCCESS;
+            /* Got all the items in the map. This is the non-error exit
+             * from the loop. */
+            // Cast OK because encoded CBOR is limited to UINT32_MAX
+         pMe->uMapEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+            // record the offset here for exit to save CPU time
+         break;
+      }
+   }
+
+Done:
+   printdecode(pMe, "GetItemsInMapBeforeDone");
+
+   
+   // Restore cursor for pre-order traversal
+   /*
+   pMe->nesting.pCurrent         = pSaveCurrent;
+   pMe->nesting.pCurrent->uCount = uSaveCount;
+   UsefulInputBuf_Seek(&(pMe->InBuf), uSave);
+    */
+   pMe->nesting = N;
+    
+   printdecode(pMe, "GetItemsInMapEnd");
+
+   return nReturn;
+}
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pMe)
+{
+   size_t uEndOffset;
+
+   if(pMe->uMapEndOffset) {
+      uEndOffset = pMe->uMapEndOffset;
+      // It is only valid once.
+      pMe->uMapEndOffset = 0;
+   } else {
+      QCBORItem Dummy;
+
+      Dummy.uLabelType = QCBOR_TYPE_NONE;
+
+      QCBORError nReturn = GetItemsInMap(pMe, &Dummy, &uEndOffset);
+
+      (void)nReturn; // TODO:
+   }
+   
+   printdecode(pMe, "start exit");
+   UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset);
+
+   if(pMe->nesting.pCurrent->uCount != UINT16_MAX) {
+      pMe->nesting.pCurrent->uCount = 1;
+   }
+   DecodeNesting_DecrementCount(&(pMe->nesting));
+   printdecode(pMe, "end exit");
+
+}
+
+
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pMe,
+                                    int64_t nLabel,
+                                    uint8_t uQcborType,
+                                    QCBORItem *pItem)
+{
+   QCBORItem One[2];
+
+   One[0].uLabelType  = QCBOR_TYPE_INT64;
+   One[0].label.int64 = nLabel;
+   One[1].uLabelType  = QCBOR_TYPE_NONE; // Indicates end of array
+
+   QCBORError nReturn = GetItemsInMap(pMe, One, NULL);
+   if(nReturn) {
+     return nReturn;
+   }
+
+   if(One[0].uDataType == QCBOR_TYPE_NONE) {
+     return QCBOR_ERR_NOT_FOUND;
+   }
+
+   if(One[0].uDataType != uQcborType) {
+     return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   *pItem = One[0];
+
+   return QCBOR_SUCCESS;
+}
+
+
+QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe,
+                                      const char *szLabel,
+                                      uint8_t uQcborType,
+                                      QCBORItem *pItem)
+{
+   QCBORItem One[2];
+
+   One[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   One[0].label.string = UsefulBuf_FromSZ(szLabel);
+   One[1].uLabelType   = QCBOR_TYPE_NONE; // Indicates end of array
+
+   QCBORError nReturn = GetItemsInMap(pMe, One, NULL);
+   if(nReturn) {
+     return nReturn;
+   }
+
+   if(One[0].uDataType == QCBOR_TYPE_NONE) {
+     return QCBOR_ERR_NOT_FOUND;
+   }
+
+   if(One[0].uDataType != uQcborType) {
+     return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   *pItem = One[0];
+
+   return QCBOR_SUCCESS;
+}
+
+
+void QCBORDecode_GetBstrInMap(QCBORDecodeContext *pMe, int64_t nLabel, UsefulBufC *pBstr)
+{
+   // TODO: error handling
+   QCBORItem Item;
+   QCBORDecode_GetItemInMap(pMe, nLabel, QCBOR_TYPE_BYTE_STRING, &Item);
+   *pBstr = Item.val.string;
+}
+
+void QCBORDecode_GetBstrInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, UsefulBufC *pBstr)
+{
+   // TODO: error handling
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_BYTE_STRING, &Item);
+   *pBstr = Item.val.string;
+}
+
+void QCBORDecode_GetTextInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, UsefulBufC *pBstr)
+{
+   // TODO: error handling
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_TEXT_STRING, &Item);
+   *pBstr = Item.val.string;
+}
+
+
+QCBORError QCBORDecode_EnterMapInMap(QCBORDecodeContext *pMe, int64_t nLabel)
+{
+   QCBORItem One[2];
+
+   One[0].uLabelType  = QCBOR_TYPE_INT64;
+   One[0].label.int64 = nLabel;
+   One[1].uLabelType  = QCBOR_TYPE_NONE;
+
+   size_t uOffset;
+
+   QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset);
+
+   if(nReturn) {
+     return nReturn;
+   }
+
+   if(One[0].uDataType != QCBOR_TYPE_MAP) {
+     return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   UsefulInputBuf_Seek(&(pMe->InBuf), uOffset);
+
+   DecodeNesting_Descend(&(pMe->nesting), &One[1]);
+
+   pMe->nesting.pCurrent->uOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+   pMe->nesting.pCurrent->uMapMode = 1;
+
+   return 0;
+}
+
+
+QCBORError QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel)
+{
+   QCBORItem One[2];
+
+   One[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   One[0].label.string = UsefulBuf_FromSZ(szLabel);
+   One[1].uLabelType   = QCBOR_TYPE_NONE;
+
+   size_t uOffset;
+
+   QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset);
+
+   if(nReturn) {
+      return nReturn;
+   }
+
+   if(One[0].uDataType != QCBOR_TYPE_MAP) {
+      return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+   
+   UsefulInputBuf_Seek(&(pMe->InBuf), uOffset);
+
+   QCBORItem MapToEnter;
+   QCBORDecode_GetNext(pMe, &MapToEnter);
+
+
+   // DecodeNesting_Descend(&(pMe->nesting), &One[1]);
+
+   pMe->nesting.pCurrent->uOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+   pMe->nesting.pCurrent->uMapMode = 1;
+   pMe->nesting.pCurrent->uSaveCount = pMe->nesting.pCurrent->uCount;
+
+   printdecode(pMe, "Entered Map in Map");
+
+   return 0;
+}
+
+
+
+
+QCBORError QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char  *szLabel)
+{
+   QCBORItem One[2];
+
+   One[0].uLabelType   = QCBOR_TYPE_TEXT_STRING;
+   One[0].label.string = UsefulBuf_FromSZ(szLabel);
+   One[1].uLabelType   = QCBOR_TYPE_NONE;
+
+   size_t uOffset;
+
+   QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset);
+
+   if(nReturn != QCBOR_SUCCESS) {
+      return nReturn;
+   }
+
+   if(One[0].uDataType != QCBOR_TYPE_ARRAY) {
+      return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+   
+   UsefulInputBuf_Seek(&(pMe->InBuf), uOffset);
+
+   QCBORItem ArrayToEnter;
+   QCBORDecode_GetNext(pMe, &ArrayToEnter);
+
+
+   // DecodeNesting_Descend(&(pMe->nesting), &One[1]);
+
+   pMe->nesting.pCurrent->uOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+   pMe->nesting.pCurrent->uMapMode = 1;
+   pMe->nesting.pCurrent->uSaveCount = pMe->nesting.pCurrent->uCount;
+
+   printdecode(pMe, "Entered Array in Map");
+
+   return 0;
+}
+
+
+
+
+/* Next item must be map or this generates an error */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pMe)
+{
+   QCBORItem  Item;
+   QCBORError nReturn;
+
+   /* Get the data item that is the map that is being searched */
+   nReturn = QCBORDecode_GetNext(pMe, &Item);
+   if(nReturn != QCBOR_SUCCESS) {
+      return nReturn;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP) {
+      return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   printdecode(pMe, "EnterMap");
+
+   // Cast to uint32_t is safe because QCBOR onl works on data < UINT32_MAX
+   pMe->nesting.pCurrent->uOffset  = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+   pMe->nesting.pCurrent->uMapMode = 1;
+   pMe->nesting.pCurrent->uSaveCount = pMe->nesting.pCurrent->uCount;
+
+
+   return QCBOR_SUCCESS;
+}
+
+
+
+QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList)
+{
+   return GetItemsInMap(pCtx, pItemList, NULL);
+}
+
+
+void QCBORDecode_GetIntInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, int64_t *pInt)
+{
+   // TODO: error handling
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe,szLabel, QCBOR_TYPE_INT64, &Item);
+   *pInt = Item.val.int64;
+}
+
+
+void QCBORDecode_RewindMap(QCBORDecodeContext *pMe)
+{
+   // TODO: check for map mode
+   pMe->nesting.pCurrent->uCount = pMe->nesting.pCurrent->uSaveCount;
+   UsefulInputBuf_Seek(&(pMe->InBuf), pMe->nesting.pCurrent->uOffset);
+}
+
+
+QCBORError QCBORDecode_EnterArray(QCBORDecodeContext *pMe)
+{
+   QCBORItem  Item;
+   QCBORError nReturn;
+
+   /* Get the data item that is the map that is being searched */
+   nReturn = QCBORDecode_GetNext(pMe, &Item);
+   if(nReturn != QCBOR_SUCCESS) {
+      return nReturn;
+   }
+   if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+      return QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   printdecode(pMe, "EnterArray");
+
+   // Cast to uint32_t is safe because QCBOR onl works on data < UINT32_MAX
+   pMe->nesting.pCurrent->uOffset  = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+   pMe->nesting.pCurrent->uMapMode = 1;
+   pMe->nesting.pCurrent->uSaveCount = pMe->nesting.pCurrent->uCount;
+
+
+   return QCBOR_SUCCESS;
+}
+
+
+void QCBORDecode_ExitArray(QCBORDecodeContext *pMe)
+{
+   // TODO: make sure we have entered an array
+   // TODO: combine with code for map? It is the same so far.
+   size_t uEndOffset;
+
+   if(pMe->uMapEndOffset) {
+      uEndOffset = pMe->uMapEndOffset;
+      // It is only valid once.
+      pMe->uMapEndOffset = 0;
+   } else {
+      QCBORItem Dummy;
+
+      Dummy.uLabelType = QCBOR_TYPE_NONE;
+
+      QCBORError nReturn = GetItemsInMap(pMe, &Dummy, &uEndOffset);
+
+      (void)nReturn; // TODO:
+   }
+   
+   printdecode(pMe, "start exit");
+   UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset);
+
+   if(pMe->nesting.pCurrent->uCount != UINT16_MAX) {
+      pMe->nesting.pCurrent->uCount = 1;
+   }
+   DecodeNesting_DecrementCount(&(pMe->nesting));
+   printdecode(pMe, "end exit");
+}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index be8586b..c2e478e 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -3787,3 +3787,94 @@
 }
 
 #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"
+   }
+  }
+ */
+   
+#include "qcbor_decode_map.h"
+#include <stdio.h>
+
+int32_t EnterMapTest()
+{
+   QCBORDecodeContext DCtx;
+   QCBORError nCBORError;
+
+   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded), 0);
+ 
+   /*
+   do {
+      QCBORItem Item;
+      
+      nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+      
+      printf("type: %d, nest %d, next: %d\n", Item.uDataType, Item.uNestingLevel, Item.uNextNestLevel);
+   } while(nCBORError == 0);
+   */
+   
+   
+   
+   QCBORDecode_EnterMap(&DCtx);
+   
+   int64_t nDecodedInt1, nDecodedInt2;
+   UsefulBufC B1, B2, S1;
+   
+   QCBORDecode_GetIntInMapSZ(&DCtx, "first integer",  &nDecodedInt1);
+   
+   QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+      
+   QCBORDecode_GetIntInMapSZ(&DCtx,  "another int",  &nDecodedInt2);
+   QCBORDecode_GetBstrInMapSZ(&DCtx,  "bytes 1",  &B1);
+   QCBORDecode_GetBstrInMapSZ(&DCtx,  "bytes 2",  &B2);
+   QCBORDecode_GetTextInMapSZ(&DCtx,  "text 2",  &S1);
+
+   QCBORDecode_ExitMap(&DCtx);
+   
+   QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+   
+   QCBORItem Item1, Item2, Item3;
+   QCBORDecode_GetNext(&DCtx, &Item1);
+   QCBORDecode_GetNext(&DCtx, &Item2);
+   if(QCBORDecode_GetNext(&DCtx, &Item3) != QCBOR_ERR_NO_MORE_ITEMS) {
+      return -400;
+   }
+
+
+   QCBORDecode_ExitArray(&DCtx);
+
+   
+   QCBORDecode_ExitMap(&DCtx);
+   
+   nCBORError = QCBORDecode_Finish(&DCtx);
+   
+   if(nCBORError) {
+      return (int32_t)nCBORError;
+   }
+   
+   if(nDecodedInt1 != 42) {
+      return 1000;
+   }
+
+   if(nDecodedInt2 != 98) {
+      return 2000;
+   }
+
+   
+   return 0;
+   
+}
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 7c8c185..76296bf 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -252,4 +252,8 @@
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 
 
+int32_t EnterMapTest(void);
+
+
+
 #endif /* defined(__QCBOR__qcbort_decode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index 5c0abe3..e46f14a 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -56,6 +56,7 @@
 
 
 static test_entry s_tests[] = {
+    TEST_ENTRY(EnterMapTest),
     TEST_ENTRY(QCBORHeadTest),
     TEST_ENTRY(EmptyMapsAndArraysTest),
     TEST_ENTRY(NotWellFormedTests),