progress...
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_map.h b/inc/qcbor/qcbor_decode_map.h
new file mode 100644
index 0000000..bb51519
--- /dev/null
+++ b/inc/qcbor/qcbor_decode_map.h
@@ -0,0 +1,192 @@
+//
+// 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_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);
+
+
+/*
+ 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 */
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index a7cb440..7c13533 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -123,9 +123,11 @@
*/
typedef struct __QCBORDecodeNesting {
// PRIVATE DATA STRUCTURE
- struct {
+ struct nesting_decode_level {
+ uint32_t uOffset;
uint16_t uCount;
uint8_t uMajorType;
+ uint8_t uMapMode;
} pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING1+1],
*pCurrent;
} QCBORDecodeNesting;
@@ -154,8 +156,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 09a220d..1115fa9 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -72,7 +72,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]));
}
@@ -1031,6 +1031,7 @@
/*
Public function, see header qcbor/qcbor_decode.h file
+ TODO: correct this comment
*/
QCBORError QCBORDecode_GetNextMapOrArray(QCBORDecodeContext *me,
QCBORItem *pDecodedItem,
@@ -1038,11 +1039,9 @@
{
// 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
if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) == 0 && !DecodeNesting_IsNested(&(me->nesting))) {
nReturn = QCBOR_ERR_NO_MORE_ITEMS;
goto Done;
@@ -1424,7 +1423,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.
@@ -1715,3 +1714,345 @@
return QCBOR_SUCCESS;
}
+
+
+
+
+/*
+ * Public function. See qcbor_util.h
+ */
+static QCBORError
+ConsumeItem(QCBORDecodeContext *pMe,
+ const QCBORItem *pItemToConsume,
+ uint_fast8_t *puNextNestLevel)
+{
+ QCBORError nReturn;
+ QCBORItem Item;
+
+ 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;
+}
+
+
+/*
+ * Public function. qcbor_util.h
+ */
+QCBORError
+GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemArray, size_t *puOffset)
+{
+ QCBORItem *pIterator;
+ QCBORError nReturn;
+
+ // 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;
+ const void *pSaveCurrent = pMe->nesting.pCurrent;
+
+ if(pMe->nesting.pCurrent->uCount != UINT16_MAX) {
+ pMe->nesting.pCurrent->uCount = 0;
+ }
+
+ 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));
+
+ if((nReturn = QCBORDecode_GetNext(pMe, &Item)) != QCBOR_SUCCESS) {
+ /* Got non-well-formed CBOR */
+ goto Done;
+ }
+
+ // Loop over all the items to check this item against
+ for(pIterator = pItemArray; pIterator->uLabelType != 0; pIterator++) {
+ if(Item.uLabelType == QCBOR_TYPE_INT64) {
+ if(Item.label.int64 != pIterator->label.int64) {
+ continue; // Label didn't match
+ }
+ } else if(Item.uLabelType == QCBOR_TYPE_TEXT_STRING) {
+ if(UsefulBuf_Compare(Item.label.string, pIterator->label.string)) {
+ continue; // Label didn't match
+ }
+ } else {
+ // Some label type that isn't supported; ignore it
+ continue;
+ }
+ // A label match has been found
+ if(pIterator->uDataType != QCBOR_TYPE_NONE) {
+ nReturn = QCBOR_ERR_DUPLICATE_LABEL;
+ goto Done;
+ }
+ *pIterator = Item;
+ if(puOffset) {
+ *puOffset = uOffset;
+ }
+ }
+
+ /* 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:
+ // Restore cursor for pre-order traversal
+ pMe->nesting.pCurrent = (struct nesting_decode_level *)pSaveCurrent;
+ pMe->nesting.pCurrent->uCount = uSaveCount;
+ UsefulInputBuf_Seek(&(pMe->InBuf), uSave);
+
+ return nReturn;
+}
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pMe)
+{
+ size_t uEndOffset;
+
+ if(pMe->uMapEndOffset) {
+ uEndOffset = pMe->uMapEndOffset;
+ } else {
+ /* Call qcbor_util_get_items_in_map to do the work. */
+ QCBORItem Dummy;
+
+ Dummy.uDataType = QCBOR_TYPE_NONE;
+
+ QCBORError nReturn = GetItemsInMap(pMe, &Dummy, &uEndOffset);
+
+ (void)nReturn; // TODO:
+ }
+ UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset);
+
+ DecodeNesting_DecrementCount(&(pMe->nesting));
+}
+
+
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ uint8_t uQcborType,
+ QCBORItem *pItem)
+{
+ /* Call qcbor_util_get_items_in_map to do the work. */
+ QCBORItem One[2];
+
+ One[0].uDataType = QCBOR_TYPE_INT64;
+ One[0].label.int64 = nLabel;
+ One[1].uDataType = 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)
+{
+ /* Call qcbor_util_get_items_in_map to do the work. */
+ QCBORItem One[2];
+
+ One[0].uDataType = QCBOR_TYPE_TEXT_STRING;
+ One[0].label.string = UsefulBuf_FromSZ(szLabel);
+ One[1].uDataType = 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;
+}
+
+
+QCBORError QCBORDecode_EnterMapInMap(QCBORDecodeContext *pMe, int64_t nLabel)
+{
+ QCBORItem One[2];
+
+ One[0].uDataType = QCBOR_TYPE_INT64;
+ One[0].label.int64 = nLabel;
+ One[1].uDataType = QCBOR_TYPE_NONE;
+
+ size_t uOffset;
+
+ QCBORError r = GetItemsInMap(pMe, One, &uOffset);
+
+ if(r) {
+ return r;
+ }
+
+ 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].uDataType = QCBOR_TYPE_TEXT_STRING;
+ One[0].label.string = UsefulBuf_FromSZ(szLabel);
+ One[1].uDataType = 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;
+}
+
+
+
+/* Next item must be map or this generates an error */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pMe)
+{
+ QCBORItem Item;
+
+ /* Get the data item that is the map that is being searched */
+ QCBORDecode_GetNext(pMe, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+
+
+ // 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;
+
+
+ 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 *pCtxt)
+{
+
+
+}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index be8586b..64c66f8 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -3787,3 +3787,62 @@
}
#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"
+
+int32_t EnterMapTest()
+{
+ QCBORDecodeContext DCtx;
+ QCBORError nCBORError;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded), 0);
+
+ QCBORDecode_EnterMap(&DCtx);
+
+ int64_t nDecodedInt1, nDecodedInt2;
+ QCBORDecode_GetIntInMapSZ(&DCtx, "first integer", &nDecodedInt1);
+
+ QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+
+ QCBORDecode_GetIntInMapSZ(&DCtx, "another int", &nDecodedInt2);
+
+ QCBORDecode_ExitMap(&DCtx);
+ QCBORDecode_ExitMap(&DCtx);
+
+ nCBORError = QCBORDecode_Finish(&DCtx);
+
+ if(nCBORError) {
+ return (int32_t)nCBORError;
+ }
+
+ if(nDecodedInt1 != 42) {
+ return 1000;
+ }
+
+ if(nDecodedInt1 != 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),