Add QCBORDecoder_Tell(); traversal cursor position bug fix (#223)
Adds QCBORDecode_Tell() to return traversal cursor position.
Fixes a traversal cursor position bug. The position was not correct after a QCBORDecode_GetXXXInX() of a non-aggregate item by label (e.g., QCBORDecode_GetInt64InMapN()) in an array that was entered.
Fixes to documentation for traversal cursor.
Addresses #213
* Add QCBORDecoder_Tell()
* Testing near complete and passing
* More tests and documentation
* Add some more documentation
* typo
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index bf866f1..6fdb70f 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -64,9 +64,9 @@
* nodes are either arrays or maps. Fundamentally, CBOR decoding is a
* pre-order traversal of this tree with CBOR sequences a minor
* exception. Calling QCBORDecode_GetNext() repeatedly will perform
- * this. It is possible to decode any CBOR by only calling
- * QCBORDecode_GetNext(), though this doesn't take advantage of many
- * QCBOR features.
+ * this. QCBOR maintains an internal traversal cursor. It is possible
+ * to decode any CBOR by only calling QCBORDecode_GetNext(), though
+ * this doesn't take advantage of many QCBOR features.
*
* QCBORDecode_GetNext() returns a 56 byte structure called
* @ref QCBORItem that describes the decoded item including:
@@ -198,8 +198,8 @@
} QCBORDecodeMode;
/**
- * The maximum size of input to the decoder. Slightly less than UINT32_MAX
- * to make room for some special indicator values.
+ * The maximum size of input to the decoder. Slightly less than
+ * @c UINT32_MAX to make room for some special indicator values.
*/
#define QCBOR_MAX_DECODE_INPUT_SIZE (UINT32_MAX - 2)
@@ -1035,6 +1035,48 @@
/**
+ * @brief Get the current traversal cursort offset in the input CBOR.
+ *
+ * @param[in] pCtx The decoder context.
+ *
+ * @returns The traversal cursor offset or @c UINT32_MAX.
+
+ * The position returned is always the start of the next item that
+ * would be next decoded with QCBORDecode_VGetNext(). If the cursor
+ * is at the end of the input or in the error state, @c UINT32_MAX is
+ * returned.
+ *
+ * When decoding map items, the position returned is always of the
+ * label, never the value.
+ *
+ * For indefinite-length arrays and maps, the break byte is consumed
+ * when the last item in the array or map is consumed so the cursor is
+ * at the next item to be decoded as expected.
+ *
+ * There are some special rules for the traversal cursor when fetching
+ * map items by label. See the description of @SpiffyDecode.
+ *
+ * When traversal is bounded because an array or map has been entered
+ * (e.g., QCBORDecode_EnterMap()) and all items in the array or map
+ * have been consumed, the position returned will be of the item
+ * outside of the array or map. The array or map must be exited before
+ * QCBORDecode_VGetNext() will decode it.
+ *
+ * In many cases the position returned will be in the middle of
+ * an array or map. It will not be possible to start decoding at
+ * that location with another instance of the decoder and go to
+ * the end. It is not valid CBOR. If the input is a CBOR sequence
+ * and the position is not in the moddle of an array or map
+ * then it is possible to decode to the end.
+ *
+ * There is no corresponding seek method because it is too complicated
+ * to restore the internal decoder state that tracks nesting.
+ */
+uint32_t
+QCBORDecode_Tell(QCBORDecodeContext *pCtx);
+
+
+/**
* @brief Returns the tag numbers for an item.
*
* @param[in] pCtx The decoder context.
@@ -1060,7 +1102,7 @@
* See also @ref CBORTags, @ref Tag-Usage and @ref Tags-Overview.
*
* To reduce memory used by a QCBORItem, tag numbers larger than
- * UINT16_MAX are mapped so the tag numbers in @c uTags should be
+ * @c UINT16_MAX are mapped so the tag numbers in @c uTags should be
* accessed with this function rather than directly.
*
* This returns @ref CBOR_TAG_INVALID64 if any error occurred when
@@ -1273,8 +1315,8 @@
* @return 0 on success -1 if not
*
* When decoding an integer, the CBOR decoder will return the value as
- * an int64_t unless the integer is in the range of @c INT64_MAX and @c
- * UINT64_MAX. That is, unless the value is so large that it can only be
+ * an int64_t unless the integer is in the range of @c INT64_MAX and
+ * @c UINT64_MAX. That is, unless the value is so large that it can only be
* represented as a @c uint64_t, it will be an @c int64_t.
*
* CBOR itself doesn't size the individual integers it carries at
@@ -1486,7 +1528,7 @@
* 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
+ * to @ref QCBOR_MAX_TAGS_PER_ITEM items whereas previously it was
* unlimited. Second, it will not inlucde the tags that QCBOR decodes
* internally.
*
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 84c4cbe..35c60a7 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -252,13 +252,13 @@
* 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.
+ * 2) Item count 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 for definite or indefinite-length
+ * Item count tracking is either 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
@@ -269,16 +269,16 @@
* uCountCursor is QCBOR_COUNT_INDICATES_ZERO_LENGTH to indicate
* it is empty.
*
- * This also records whether a level is bounded or not. All
+ * 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.
+ * bstr-wrapped CBOR) are bounded implicitly. 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 @c 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
+ * union/struct will be 8 bytes rather than 9 and a lot of
* wasted padding for alignment will be saved.
*/
uint8_t uLevelType;
@@ -289,6 +289,8 @@
uint16_t uCountTotal;
uint16_t uCountCursor;
#define QCBOR_NON_BOUNDED_OFFSET UINT32_MAX
+ /* The start of the array or map in bounded mode so
+ * the input can be rewound for GetInMapXx() by label. */
uint32_t uStartOffset;
} ma; /* for maps and arrays */
struct {
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index 84fb516..32a8ef5 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -91,9 +91,6 @@
* QCBORDecode_EnterArray() can be used to narrow the traversal to the
* extent of the array.
*
- * QCBORDecode_EnterArray() can be used to narrow the traversal to the
- * extent of the array.
- *
* All the QCBORDecode_GetXxxxInMapX() methods support duplicate label
* detection and will result in an error if the map has duplicate
* labels.
@@ -701,7 +698,7 @@
* 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
+ * This sets the traversal cursor to the item after the
* array that was exited.
*
* This will result in an error if any item in the array is not well
@@ -740,7 +737,7 @@
* fully exited.
*
* While in bounded mode, QCBORDecode_GetNext() works as usual on the
- * map and the pre-order traversal cursor is maintained. It starts out
+ * map and the 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
@@ -751,12 +748,12 @@
* non-aggregate items by label behaves differently from entering subordinate
* aggregate items by label. See dicussion in @ref SpiffyDecode.
*
- * Exiting leaves the pre-order cursor at the data item following the
+ * Exiting leaves the traversal 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
+ * its contents. After QCBORDecode_ExitMap(), the traversal
* cursor will be at the first item after the map.
*
* Please see @ref Decode-Errors-Overview "Decode Errors Overview".
@@ -786,7 +783,7 @@
* The items in the map 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 map
+ * This sets the traversal cursor to the item after the map
* that was exited.
*
* This will result in an error if any item in the map is not well
@@ -845,7 +842,7 @@
* with little nesting, this is of little consequence, but may be of
* consequence for large deeply nested CBOR structures on slow CPUs.
*
- * The position of the pre-order traversal cursor is not changed.
+ * The position of the traversal cursor is not changed.
*
* Please see @ref Decode-Errors-Overview "Decode Errors Overview".
*
@@ -892,7 +889,7 @@
* QCBORDecode_EnterMapinMapN(), QCBORDecode_EnterArrayInMapN() and
* such to descend into and process maps and arrays.
*
- * The position of the pre-order traversal cursor is not changed.
+ * The position of the traversal cursor is not changed.
*
* Please see @ref Decode-Errors-Overview "Decode Errors Overview".
*
@@ -1825,7 +1822,7 @@
* The items in the wrapped CBOR that was entered do not have to have
* been consumed for this to succeed.
*
- * The this sets the pre-order traversal cursor to the item after the
+ * The this sets the traversal cursor to the item after the
* byte string that was exited.
*/
void
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 2188211..799fff2 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -3132,6 +3132,29 @@
}
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+uint32_t
+QCBORDecode_Tell(QCBORDecodeContext *pMe)
+{
+ size_t uCursorOffset;
+
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return UINT32_MAX;
+ }
+
+ uCursorOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+ if(uCursorOffset == UsefulInputBuf_GetBufferLength(&(pMe->InBuf))) {
+ return UINT32_MAX;
+ } else {
+ /* Cast is safe because decoder input size is restricted. */
+ return (uint32_t)uCursorOffset;
+ }
+}
+
+
/**
* @brief Rewind cursor to start as if map or array were just entered.
*
@@ -3262,6 +3285,7 @@
}
QCBORDecodeNesting SaveNesting;
+ size_t uSavePos = UsefulInputBuf_Tell(&(pMe->InBuf));
DecodeNesting_PrepareForMapSearch(&(pMe->nesting), &SaveNesting);
/* Reposition to search from the start of the map / array */
@@ -3378,6 +3402,7 @@
Done:
DecodeNesting_RestoreFromMapSearch(&(pMe->nesting), &SaveNesting);
+ UsefulInputBuf_Seek(&(pMe->InBuf), uSavePos);
Done2:
/* For all items not found, set the data and label type to QCBOR_TYPE_NONE */
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index fa3486e..af83099 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -474,7 +474,7 @@
/* This expression integer-promotes to type int. The code above in
* function guarantees that nAdditionalInfo will never be larger
* than 0x1f. The caller may pass in a too-large uMajor type. The
- * conversion to unint8_t will cause an integer wrap around and
+ * conversion to uint8_t will cause an integer wrap around and
* incorrect CBOR will be generated, but no security issue will
* occur.
*/
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 16ab10f..7b3b0f3 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -5996,6 +5996,17 @@
{
QCBORDecodeContext DCtx;
QCBORItem Item1;
+ int64_t nInt;
+ QCBORError uErr;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spNested), 0);
+ QCBORDecode_EnterMap(&DCtx, NULL);
+ QCBORDecode_GetInt64InMapN (&DCtx, 3, &nInt);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item1);
+ if(Item1.uDataType != QCBOR_TYPE_INT64) {
+ return 700;
+ }
+
int i;
for(i = 0; i < 13; i++) {
@@ -8911,3 +8922,312 @@
return 0;
}
+
+
+int32_t TellTests(void)
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ uint32_t uPosition;
+ int nIndex;
+ int64_t nDecodedInt;
+
+ static const uint32_t aPos[] =
+ {0, 1, 17, 42, 50, 58, 72, 85, 98, 112, UINT32_MAX};
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+ 0);
+ for(nIndex = 0; ; nIndex++) {
+ uPosition = QCBORDecode_Tell(&DCtx);
+ if(uPosition != aPos[nIndex]) {
+ return nIndex;
+ }
+
+ if(uPosition == UINT32_MAX) {
+ break;
+ }
+
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ }
+
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+ static const uint32_t aPosIndef[] =
+ {0, 1, 17, 42, 50, 59, 73, 86, 99, 113, UINT32_MAX};
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapIndefEncoded),
+ 0);
+
+ for(nIndex = 0; ; nIndex++) {
+ uPosition = QCBORDecode_Tell(&DCtx);
+ if(uPosition != aPosIndef[nIndex]) {
+ return nIndex + 100;
+ }
+
+ if(uPosition == UINT32_MAX) {
+ break;
+ }
+
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ }
+#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+
+ /* Next, some tests with entered maps and arrays */
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+ 0);
+ QCBORDecode_EnterMap(&DCtx, &Item);
+ if(QCBORDecode_Tell(&DCtx) != 1) {
+ return 1001;
+ }
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "first integer", &nDecodedInt);
+ if(QCBORDecode_Tell(&DCtx) != 1) {
+ return 1002;
+ }
+ QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+ if(QCBORDecode_Tell(&DCtx) != 72) {
+ return 1003;
+ }
+
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "another int", &nDecodedInt);
+ if(nDecodedInt != 98) {
+ return 1004;
+ }
+ /* Getting non-aggregate types doesn't affect cursor position. */
+ if(QCBORDecode_Tell(&DCtx) != 72) {
+ return 1005;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_Tell(&DCtx) != 85) {
+ return 1006;
+ }
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "another int", &nDecodedInt);
+ if(nDecodedInt != 98) {
+ return 1007;
+ }
+ /* Getting non-aggregate types doesn't affect cursor position. */
+ if(QCBORDecode_Tell(&DCtx) != 85) {
+ return 1008;
+ }
+
+ QCBORDecode_ExitMap(&DCtx);
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 1009;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 1010;
+ }
+
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+ /* Next, some tests with entered maps and arrays */
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapIndefEncoded),
+ 0);
+ QCBORDecode_EnterMap(&DCtx, &Item);
+ if(QCBORDecode_Tell(&DCtx) != 1) {
+ return 2000;
+ }
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "first integer", &nDecodedInt);
+ if(QCBORDecode_Tell(&DCtx) != 1) {
+ return 2001;
+ }
+ QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+ if(QCBORDecode_Tell(&DCtx) != 73) {
+ return 2002;
+ }
+
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "another int", &nDecodedInt);
+ if(nDecodedInt != 98) {
+ return 2003;
+ }
+ /* Getting non-aggregate types doesn't affect cursor position. */
+ if(QCBORDecode_Tell(&DCtx) != 73) {
+ return 2004;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_Tell(&DCtx) != 86) {
+ return 2005;
+ }
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "another int", &nDecodedInt);
+ if(nDecodedInt != 98) {
+ return 2006;
+ }
+ /* Getting non-aggregate types doesn't affect cursor position. */
+ if(QCBORDecode_Tell(&DCtx) != 86) {
+ return 2007;
+ }
+
+ QCBORDecode_ExitMap(&DCtx);
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 2008;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 2010;
+ }
+#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+
+
+
+ /* Error state test */
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+ 0);
+ /* Cause an error */
+ QCBORDecode_GetInt64InMapSZ(&DCtx, "another int", &nDecodedInt);
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 3000;
+ }
+
+ /* Empties tests */
+ const uint8_t pMinimalCBOR[] = {0xa0}; // One empty map
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pMinimalCBOR),0);
+ if(QCBORDecode_Tell(&DCtx) != 0) {
+ return 4000;
+ }
+ QCBORDecode_EnterMap(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 4001;
+ }
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 4002;
+ }
+ QCBORDecode_ExitMap(&DCtx);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 4001;
+ }
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 4002;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 4010;
+ }
+
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+ const uint8_t pMinimalIndefCBOR[] = {0xbf, 0xff}; // One empty map
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pMinimalIndefCBOR),0);
+ if(QCBORDecode_Tell(&DCtx) != 0) {
+ return 4100;
+ }
+ QCBORDecode_EnterMap(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 4101;
+ }
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 4102;
+ }
+ QCBORDecode_ExitMap(&DCtx);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 4101;
+ }
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 4102;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 4110;
+ }
+#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+
+ /* Test on a CBOR sequence */
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSequenceTestInput),0);
+ if(QCBORDecode_Tell(&DCtx) != 0) {
+ return 5000;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 5001;
+ }
+ if(QCBORDecode_Tell(&DCtx) != 11) {
+ return 5002;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 5003;
+ }
+ if(QCBORDecode_Tell(&DCtx) != 12) {
+ return 5004;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 5005;
+ }
+ if(QCBORDecode_Tell(&DCtx) != 17) {
+ return 5006;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 5007;
+ }
+ if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+ return 5008;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 5010;
+ }
+
+
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+ 0);
+ QCBORDecode_EnterMap(&DCtx, &Item);
+ QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+ if(QCBORDecode_Tell(&DCtx) != 42) {
+ return 6001;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_Tell(&DCtx) != 50) {
+ return 6002;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ if(QCBORDecode_Tell(&DCtx) != 58) {
+ return 6008;
+ }
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ (void)QCBORDecode_GetAndResetError(&DCtx);
+ if(QCBORDecode_Tell(&DCtx) != 58) {
+ return 6003;
+ }
+ QCBORDecode_ExitArray(&DCtx);
+ if(QCBORDecode_Tell(&DCtx) != 58) {
+ return 6004;
+ }
+
+ static const uint32_t aEmptiesPos[] =
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, UINT32_MAX};
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmpties),
+ 0);
+ for(nIndex = 0; ; nIndex++) {
+ uPosition = QCBORDecode_Tell(&DCtx);
+ if(uPosition != aEmptiesPos[nIndex]) {
+ return nIndex + 200;
+ }
+
+ if(uPosition == UINT32_MAX) {
+ break;
+ }
+
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ }
+
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+ static const uint32_t aIndefEmptiesPos[] =
+ {0, 1, 2, 4, 5, 7, 8, 10, 12, 13, 16, 19, UINT32_MAX};
+ QCBORDecode_Init(&DCtx,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmptiesIndef),
+ 0);
+ for(nIndex = 0; ; nIndex++) {
+ uPosition = QCBORDecode_Tell(&DCtx);
+ if(uPosition != aIndefEmptiesPos[nIndex]) {
+ return nIndex + 300;
+ }
+
+ if(uPosition == UINT32_MAX) {
+ break;
+ }
+
+ QCBORDecode_VGetNext(&DCtx, &Item);
+ }
+#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+
+
+ return 0;
+}
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 0cedf43..8d26c98 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -323,4 +323,7 @@
int32_t ErrorHandlingTests(void);
+int32_t TellTests(void);
+
+
#endif /* defined(__QCBOR__qcbort_decode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index 988fd36..5d17cde 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -1,7 +1,7 @@
/*==============================================================================
run_tests.c -- test aggregator and results reporting
- Copyright (c) 2018-2021, Laurence Lundblade. All rights reserved.
+ Copyright (c) 2018-2024, Laurence Lundblade. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
@@ -66,8 +66,9 @@
static test_entry s_tests[] = {
- TEST_ENTRY(ErrorHandlingTests),
- TEST_ENTRY(OpenCloseBytesTest),
+ TEST_ENTRY(TellTests),
+ TEST_ENTRY(ErrorHandlingTests),
+ TEST_ENTRY(OpenCloseBytesTest),
TEST_ENTRY(EnterBstrTest),
TEST_ENTRY(IntegerConvertTest),
TEST_ENTRY(EnterMapTest),