Clean up / rearrange of code for tracking nesting; fixed a bug too
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index 17dc1f2..dfcdcf4 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -7,7 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
- 0F60E27B2164705400483952 /* qcbor_decode_malloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */; };
+ 0F58EB9C216A388E002FD6D1 /* qcbor_decode_malloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F58EB9B216A388E002FD6D1 /* qcbor_decode_malloc.c */; };
E73B5756216071900080D658 /* bstrwrap_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B5755216071900080D658 /* bstrwrap_tests.c */; };
E73B57592161CA690080D658 /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
E73B575E2161CA7C0080D658 /* half_precision_test.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* half_precision_test.c */; };
@@ -33,7 +33,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qcbor_decode_malloc.c; sourceTree = "<group>"; };
+ 0F58EB9B216A388E002FD6D1 /* qcbor_decode_malloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_decode_malloc.c; path = src/qcbor_decode_malloc.c; sourceTree = "<group>"; };
E73B5754216071900080D658 /* bstrwrap_tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = bstrwrap_tests.h; path = test/bstrwrap_tests.h; sourceTree = "<group>"; };
E73B5755216071900080D658 /* bstrwrap_tests.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = bstrwrap_tests.c; path = test/bstrwrap_tests.c; sourceTree = "<group>"; };
E73B57572161CA680080D658 /* ieee754.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ieee754.h; path = src/ieee754.h; sourceTree = "<group>"; };
@@ -90,7 +90,7 @@
E776E08B214AE06600E67947 /* src */ = {
isa = PBXGroup;
children = (
- 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */,
+ 0F58EB9B216A388E002FD6D1 /* qcbor_decode_malloc.c */,
E73B57582161CA690080D658 /* ieee754.c */,
E73B57572161CA680080D658 /* ieee754.h */,
E776E08E214AE07500E67947 /* qcbor_decode.c */,
@@ -183,7 +183,7 @@
buildActionMask = 2147483647;
files = (
E776E08F214AE07500E67947 /* qcbor_encode.c in Sources */,
- 0F60E27B2164705400483952 /* qcbor_decode_malloc.c in Sources */,
+ 0F58EB9C216A388E002FD6D1 /* qcbor_decode_malloc.c in Sources */,
E73B57592161CA690080D658 /* ieee754.c in Sources */,
E73B575F2161CA7C0080D658 /* half_to_double_from_rfc7049.c in Sources */,
E73B57652161F8F80080D658 /* run_tests.c in Sources */,
diff --git a/inc/qcbor.h b/inc/qcbor.h
index 4ea4079..d8bf35f 100644
--- a/inc/qcbor.h
+++ b/inc/qcbor.h
@@ -677,7 +677,7 @@
uint64_t uint64; /** The value for uDataType QCBOR_TYPE_UINT64 */
UsefulBufC string; /** The value for uDataType QCBOR_TYPE_BYTE_STRING and QCBOR_TYPE_TEXT_STRING */
- uint16_t uCount; /** The "value" for uDataType QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP -- the number of items in the array or map */
+ uint16_t uCount; /** The "value" for uDataType QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP -- the number of items in the array or map */ // TODO: indefinite len arrays
float fnum; /** The value for uDataType QCBOR_TYPE_FLOAT */
double dfnum; /** The value for uDataType QCBOR_TYPE_DOUBLE */
struct {
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index eccd469..4679ec0 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -80,7 +80,7 @@
/*
- Collection of functions to track the map and array nesting for decoding
+ Collection of functions to track the map/array nesting for decoding
*/
inline static int IsMapOrArray(uint8_t uDataType)
@@ -95,13 +95,14 @@
inline static int DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
{
- if(!DecodeNesting_IsNested(pNesting)) {
- return 0;
- }
-
return pNesting->pCurrent->uCount == UINT16_MAX;
}
+inline static uint8_t DecodeNesting_GetLevel(QCBORDecodeNesting *pNesting)
+{
+ return pNesting->pCurrent - &(pNesting->pMapsAndArrays[0]);
+}
+
inline static int DecodeNesting_TypeIsMap(const QCBORDecodeNesting *pNesting)
{
if(!DecodeNesting_IsNested(pNesting)) {
@@ -111,54 +112,82 @@
return CBOR_MAJOR_TYPE_MAP == pNesting->pCurrent->uMajorType;
}
-inline static void DecodeNesting_Ascend(QCBORDecodeNesting *pNesting)
+// Process a break. This will either ascend the nesting or error out
+inline static int DecodeNesting_BreakAscend(QCBORDecodeNesting *pNesting)
{
- pNesting->pCurrent--;
-}
-
-inline static void DecodeNesting_Decrement(QCBORDecodeNesting *pNesting)
-{
+ // breaks must always occur when there is nesting
if(!DecodeNesting_IsNested(pNesting)) {
- return; // at top level where there is no tracking
+ return QCBOR_ERR_BAD_BREAK;
}
- // Decrement
+ // 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
+ pNesting->pCurrent--;
+
+ return QCBOR_SUCCESS;
+}
+
+// Called on every single item except breaks including the opening of a map/array
+inline static void DecodeNesting_DecrementCount(QCBORDecodeNesting *pNesting)
+{
+ if(!DecodeNesting_IsNested(pNesting)) {
+ // at top level where there is no tracking
+ return;
+ }
+
+ if(DecodeNesting_IsIndefiniteLength(pNesting)) {
+ // There is no count for indefinite length arrays/maps
+ return;
+ }
+
+ // Decrement the count of items in this array/map
pNesting->pCurrent->uCount--;
- // Pop up nesting levels if the counts at the levels is zero
- while(0 == pNesting->pCurrent->uCount && DecodeNesting_IsNested(pNesting)) {
+ // Pop up nesting levels if the counts at the levels are zero
+ while(DecodeNesting_IsNested(pNesting) && 0 == pNesting->pCurrent->uCount) {
pNesting->pCurrent--;
}
}
-inline static int DecodeNesting_Descend(QCBORDecodeNesting *pNesting, uint8_t uMajorType, int uCount)
+// Called on every map/array
+inline static int DecodeNesting_Descend(QCBORDecodeNesting *pNesting, QCBORItem *pItem)
{
int nReturn = QCBOR_SUCCESS;
- if(uCount > QCBOR_MAX_ITEMS_IN_ARRAY) {
+ if(pItem->val.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;
+ // Empty indefinite length maps and arrays are handled elsewhere; TODO: where?
+ }
+
+ // 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;
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;
goto Done;
}
+ // The actual descend
pNesting->pCurrent++;
- pNesting->pCurrent->uMajorType = uMajorType;
- pNesting->pCurrent->uCount = uCount;
+ // Record a few details for this nesting level
+ pNesting->pCurrent->uMajorType = pItem->uDataType;
+ pNesting->pCurrent->uCount = pItem->val.uCount;
Done:
return nReturn;;
}
-inline static uint8_t DecodeNesting_GetLevel(QCBORDecodeNesting *pNesting)
-{
- return pNesting->pCurrent - &(pNesting->pMapsAndArrays[0]);
-}
-
inline static void DecodeNesting_Init(QCBORDecodeNesting *pNesting)
{
pNesting->pCurrent = &(pNesting->pMapsAndArrays[0]);
@@ -612,8 +641,11 @@
static int GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
{
int nReturn;
+ UsefulBufC FullString = NULLUsefulBufC;
QCBORStringAllocator *pAlloc = (QCBORStringAllocator *)me->pStringAllocator;
+ // TODO: can this call to GetNext_Item go in the loop below so there is only
+ // one call to it so it can inline?
nReturn = GetNext_Item(&(me->InBuf), pDecodedItem, me->bStringAllocateAll ? pAlloc: NULL);
if(nReturn) {
goto Done;
@@ -645,7 +677,6 @@
const uint8_t uStringType = pDecodedItem->uDataType;
// Loop getting segments of indefinite string
- UsefulBufC FullString = NULLUsefulBufC;
for(;;) {
// Get item for next segment
QCBORItem Item;
@@ -698,6 +729,7 @@
{
int nReturn;
+ // TODO: optimize loop below so there is only one call to GetNext_FullItem
nReturn = GetNext_FullItem(me, pDecodedItem);
if(nReturn) {
goto Done;
@@ -854,47 +886,38 @@
{
int nReturn;
- if(!UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
- nReturn = QCBOR_ERR_HIT_END;
- goto Done;
- }
-
- nReturn = GetNext_MapEntry(me, pDecodedItem);
- if(nReturn) {
- goto Done;
- }
-
- // Handle any breaks that are closing out arrays or maps
- while(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
- if(!DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
- nReturn = QCBOR_ERR_BAD_BREAK;
+ // Loop getting items until one that is not a break is fetched
+ for(;;) {
+ nReturn = GetNext_MapEntry(me, pDecodedItem);
+ if(nReturn) {
goto Done;
}
- // Close out and ascend
- DecodeNesting_Ascend(&(me->nesting));
-
- nReturn = GetNext_MapEntry(me, pDecodedItem);
+ if(pDecodedItem->uDataType != QCBOR_TYPE_BREAK) {
+ break;
+ }
+
+ // Item is a break. That means we have to try to reduce
+ // nesting level.
+ nReturn = DecodeNesting_BreakAscend(&(me->nesting));
if(nReturn) {
goto Done;
}
}
- // Now have the next item that is not a break. The one that is going to be returned
+ // Now have the next item that is not a break, the one that is going to be returned
- // Record the nesting level for this data item
+ // Record the nesting level for this data item before processing any of
+ // decrementing and decsending
pDecodedItem->uNestingLevel = DecodeNesting_GetLevel(&(me->nesting));
- // If the new item is a non-empty array or map, the nesting level descends
- if(IsMapOrArray(pDecodedItem->uDataType) && pDecodedItem->val.uCount) {
- nReturn = DecodeNesting_Descend(&(me->nesting), pDecodedItem->uDataType, pDecodedItem->val.uCount);
- } else {
- // Track number of items in maps and arrays and ascend nesting if all are consumed
- // Note that an empty array or map is like an integer or string in effect here
- // No counting is done for indefinite length arrays
- if(!DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
- DecodeNesting_Decrement(&(me->nesting));
- }
+ // Always decrement the count at the current level no matter what type
+ // except for breaks as breaks are always processed above
+ DecodeNesting_DecrementCount(&(me->nesting));
+
+ // If the new item is array or map, the nesting level descends
+ if(IsMapOrArray(pDecodedItem->uDataType)) {
+ nReturn = DecodeNesting_Descend(&(me->nesting), pDecodedItem);
}
Done:
@@ -912,14 +935,21 @@
// Consume any breaks ending indefinite length arrays or maps here
// Definite length arrays and maps get closed off above when they
// the last item in them is consumed; they are not handled here.
+ // TODO: put in a function and make common with above similar code
while(DecodeNesting_GetLevel(&(me->nesting))) {
QCBORItem Item;
- GetNext_Item(&(me->InBuf), &Item, false);
+ int nReturn = GetNext_Item(&(me->InBuf), &Item, NULL);
+ if(nReturn) {
+ break;
+ }
if(Item.uDataType != QCBOR_TYPE_BREAK) {
nReturn = QCBOR_ERR_EXTRA_BYTES;
break;
}
- DecodeNesting_Ascend(&(me->nesting));
+ nReturn = DecodeNesting_BreakAscend(&(me->nesting));
+ if(nReturn) {
+ break;
+ }
}
// Call the desctructor for the string allocator if there is one