Add getting un decoded arrays and maps (#117)
This adds QCBORDecode_GetArray() and QCBORDecode_GetMap() plus friends. They return undecoded arrays and maps so they can be passed on to other decoder instances or such.
This also adds UsefulBuf_OffsetToPointer() and UsefulInputBuf_PointerToOffset() to UsefulBuf.
Consume no longer validates standard tags like big floats when it is consuming stuff. It still checks for CBOR that is not well formed.
There was some internal restructuring of array and map processing, but the tests are thorough and they are all passing.
This is to address #112
* getting started on returning arrays
* progress...
* returning maps and arrays mostly working
* Checkpoint progress
* Checkpoint: mostly working
* checkpoint -- it is working
* OffsetToPtr documentation and test
* test and documentation
* Fix Get() of nested indef arrays and maps
* Test tags; doc fixes
* Fix #ifdef test fan out
* Last bit of documentation update
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 5f53044..b322e39 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -1,6 +1,6 @@
/* =========================================================================
* Copyright (c) 2016-2018, The Linux Foundation.
- * Copyright (c) 2018-2022, Laurence Lundblade.
+ * Copyright (c) 2018-2024, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited. All rights reserved.
* All rights reserved.
*
@@ -43,6 +43,7 @@
when who what, where, why
-------- ---- --------------------------------------------------
+ 10/05/2024 llundblade Add Xxx_OffsetToPointer.
19/12/2022 llundblade Document that adding empty data is allowed.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf.
9/21/2021 llundbla Clarify UsefulOutBuf size calculation mode
@@ -647,16 +648,27 @@
/**
- @brief Convert a pointer to an offset with bounds checking.
-
- @param[in] UB Pointer to the UsefulInputBuf.
- @param[in] p Pointer to convert to offset.
-
- @return SIZE_MAX if @c p is out of range, the byte offset if not.
+ * @brief Convert a pointer to an offset with bounds checking.
+ *
+ * @param[in] UB A UsefulBuf.
+ * @param[in] p Pointer to convert to offset.
+ *
+ * @return SIZE_MAX if @c p is out of range, the byte offset if not.
*/
static inline size_t UsefulBuf_PointerToOffset(UsefulBufC UB, const void *p);
+/**
+ * @brief Convert an offset to a pointer with bounds checking.
+ *
+ * @param[in] UB A UsefulBuf.
+ * @param[in] uOffset Offset in @c pUInBuf.
+ *
+ * @return @c NULL if @c uOffset is out of range, a pointer into the buffer if not.
+ */
+static inline const void *UsefulBuf_OffsetToPointer(UsefulBufC UB, size_t uOffset);
+
+
#ifndef USEFULBUF_DISABLE_DEPRECATED
/** Deprecated macro; use @ref UsefulBuf_FROM_SZ_LITERAL instead */
#define SZLiteralToUsefulBufC(szString) UsefulBuf_FROM_SZ_LITERAL(szString)
@@ -1491,7 +1503,18 @@
*
* @return SIZE_MAX if @c p is out of range, the byte offset if not.
*/
-static inline size_t UsefulInputBuf_PointerToOffset(UsefulInputBuf *pUInBuf, const void *p);
+static size_t UsefulInputBuf_PointerToOffset(UsefulInputBuf *pUInBuf, const void *p);
+
+
+/**
+ * @brief Convert an offset to a pointer with bounds checking.
+ *
+ * @param[in] pUInBuf Pointer to the @ref UsefulInputBuf.
+ * @param[in] uOffset Offset in @c pUInBuf.
+ *
+ * @return @c NULL if @c uOffset is out of range, a pointer into the buffer if not.
+ */
+static const void *UsefulInputBuf_OffsetToPointer(UsefulInputBuf *pUInBuf, size_t uOffset);
/**
@@ -1857,6 +1880,18 @@
}
+static inline const void *UsefulBuf_OffsetToPointer(UsefulBufC UB, size_t uOffset)
+{
+ if(UsefulBuf_IsNULLC(UB) || uOffset >= UB.len) {
+ return NULL;
+ }
+
+ return (const uint8_t *)UB.ptr + uOffset;
+}
+
+
+
+
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
static inline uint32_t UsefulBufUtil_CopyFloatToUint32(float f)
{
@@ -2263,6 +2298,12 @@
}
+static inline const void *UsefulInputBuf_OffsetToPointer(UsefulInputBuf *pUInBuf, size_t uOffset)
+ {
+ return UsefulBuf_OffsetToPointer(pUInBuf->UB, uOffset);
+ }
+
+
static inline UsefulBufC UsefulInputBuf_GetUsefulBuf(UsefulInputBuf *pMe, size_t uNum)
{
const void *pResult = UsefulInputBuf_GetBytes(pMe, uNum);
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 6fdb70f..b56bb16 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -981,10 +981,19 @@
* @param[in] pCtx The decoder context.
* @param[out] pDecodedItem The decoded CBOR item.
*
- * This is the same as QCBORDecode_VGetNext() but the contents of the
- * entire map or array will be consumed if the item is a map or array.
+ * @c pItem returned is the same as QCBORDecode_VGetNext(). If the
+ * item is an array or map, the entire contents of the array or map
+ * will be consumed leaving the cursor after the array or map.
*
- * In order to go back to decode the contents of a map or array
+ * If an array or map is being consumed by this, an error will occur
+ * if any of the items in the array or map are in error.
+ *
+ * If the item is a tag the contents of which is an array or map, like
+ * a big float, @c pItem will identify it as such and the contents
+ * will be consumed, but the validity of the tag won't be checked
+ * other than for being well-formed.
+ *
+ * In order to go back to decode the contents of an array or map
* consumed by this, the decoder must be rewound using
* QCBORDecode_Rewind().
*/
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index b374e9e..ebb4b65 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -675,8 +675,8 @@
* Nested arrays and maps may be entered to a depth of
* @ref QCBOR_MAX_ARRAY_NESTING.
*
- * See also QCBORDecode_ExitArray(), QCBORDecode_EnterMap() and
- * QCBORDecode_EnterBstrWrapped().
+ * See also QCBORDecode_ExitArray(), QCBORDecode_EnterMap(),
+ * QCBORDecode_EnterBstrWrapped() and QCBORDecode_GetArray().
*/
static void
QCBORDecode_EnterArray(QCBORDecodeContext *pCtx, QCBORItem *pItem);
@@ -710,6 +710,65 @@
+/**
+ * @brief Get the encoded bytes that make up an array.
+ *
+ * @param[in] pCtx The decode context.
+ * @param[out] pItem Place to return the item.
+ * @param[out] pEncodedCBOR Place to return pointer and length of the array.
+ *
+ * The next item to decode must be an array.
+ *
+ * The encoded bytes of the array will be returned. They can be
+ * decoded by another decoder instance.
+ *
+ * @c pItem will have the label and tags for the array. It is filled
+ * in the same as if QCBORDecode_GetNext() were called on the array item. In
+ * particular, the array count will be filled in for definite-length
+ * arrays and set to @c UINT16_MAX for indefinite-length arrays.
+ *
+ * This works on both definite and indefinite length arrays (unless
+ * indefinite length array decoding has been disabled).
+ *
+ * The pointer returned is to the data item that opens the array. The
+ * length in bytes includes it and all the member data items. If the array
+ * occurs in another map and thus has a label, the label is not included
+ * in what is returned.
+ *
+ * If the array is preceeded by tags, those encoded tags are included
+ * in the encoded CBOR that is returned.
+ *
+ * QCBORDecode_GetArray() consumes the entire array and leaves the
+ * traversal cursor at the item after the array.
+ * QCBORDecode_GetArrayFromMapN() and QCBORDecode_GetArrayFromMapSZ()
+ * don't affect the traversal cursor.
+ *
+ * This traverses the whole array and every subordinate array or map in
+ * it. This is necessary to determine the length of the array.
+ *
+ * This will fail if any item in the array is not well-formed.
+ *
+ * This uses a few hundred bytes of stack, more than most methods.
+ *
+ * See also QCBORDecode_EnterArray().
+ */
+static void
+QCBORDecode_GetArray(QCBORDecodeContext *pCtx,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+static void
+QCBORDecode_GetArrayFromMapN(QCBORDecodeContext *pCtx,
+ int64_t nLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+static void
+QCBORDecode_GetArrayFromMapSZ(QCBORDecodeContext *pCtx,
+ const char *szLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
/**
* @brief Enter a map for decoding and searching.
@@ -762,6 +821,8 @@
* QCBORDecode_EnterBstrWrapped(). Entering and exiting any nested
* combination of maps, arrays and bstr-wrapped CBOR is supported up
* to the maximum of @ref QCBOR_MAX_ARRAY_NESTING.
+ *
+ * See also QCBORDecode_GetMap().
*/
static void
QCBORDecode_EnterMap(QCBORDecodeContext *pCtx, QCBORItem *pItem);
@@ -795,6 +856,67 @@
/**
+ * @brief Get the bytes that make up a map.
+ *
+ * @param[in] pCtx The decode context.
+ * @param[out] pItem Place to return the item.
+ * @param[out] pEncodedCBOR Place to return pointer and length of the map.
+ *
+ * The next item to decode must be a map.
+ *
+ * The encoded bytes of the map will be returned. They can be
+ * decoded by another decoder instance.
+ *
+ * @c pItem will have the label and tags for the array. It is filled
+ * in the same as if QCBORDecode_GetNext() were called on the map item. In
+ * particular, the map count will be filled in for definite-length
+ * maps and set to @c UINT16_MAX for indefinite-length maps.
+ *
+ * This works on both definite and indefinite length maps (unless
+ * indefinite length map decoding has been disabled).
+ *
+ * The pointer returned is to the data item that opens the map. The
+ * length in bytes includes it and all the member data items. If the map
+ * occurs in another map and thus has a label, the label is not included
+ * in what is returned.
+ *
+ * If the map is preceeded by tags, those encoded tags are included in
+ * the encoded CBOR that is returned.
+ *
+ * QCBORDecode_GetMap() consumes the entire array and leaves the
+ * traversal cursor at the item after the map.
+ * QCBORDecode_GetMapFromMapN() and QCBORDecode_GetMapFromMapSZ()
+ * don't affect the traversal cursor.
+ *
+ * This traverses the whole map and every subordinate array or map in
+ * it. This is necessary to determine the length of the map. The
+ * traversal cursor is left at the first item after the map.
+ *
+ * This will fail if any item in the map is not well-formed.
+ *
+ * This uses a few hundred bytes of stack, more than most methods.
+ *
+ * See also QCBORDecode_EnterMap().
+ */
+static void
+QCBORDecode_GetMap(QCBORDecodeContext *pCtx,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+static void
+QCBORDecode_GetMapFromMapN(QCBORDecodeContext *pCtx,
+ int64_t nLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+static void
+QCBORDecode_GetMapFromMapSZ(QCBORDecodeContext *pCtx,
+ const char *szLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+
+/**
* @brief Reset traversal cursor to start of map, array, byte-string
* wrapped CBOR or start of input.
*
@@ -2067,6 +2189,103 @@
}
+/* Semi-private funcion used by public inline functions. See qcbor_decode.c */
+void
+QCBORDecode_Private_GetArrayOrMap(QCBORDecodeContext *pCtx,
+ uint8_t uType,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+
+/* Semi-private funcion used by public inline functions. See qcbor_decode.c */
+void
+QCBORDecode_Private_SearchAndGetArrayOrMap(QCBORDecodeContext *pCtx,
+ QCBORItem *pTarget,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR);
+
+
+static inline void
+QCBORDecode_GetArray(QCBORDecodeContext *pMe,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR)
+{
+ QCBORDecode_Private_GetArrayOrMap(pMe, QCBOR_TYPE_ARRAY, pItem, pEncodedCBOR);
+}
+
+
+static inline void
+QCBORDecode_GetArrayFromMapN(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR)
+{
+ QCBORItem OneItemSeach[2];
+ OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64;
+ OneItemSeach[0].label.int64 = nLabel;
+ OneItemSeach[0].uDataType = QCBOR_TYPE_ARRAY;
+ OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE;
+
+ QCBORDecode_Private_SearchAndGetArrayOrMap(pMe, OneItemSeach, pItem, pEncodedCBOR);
+}
+
+
+static inline void
+QCBORDecode_GetArrayFromMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR)
+{
+ QCBORItem OneItemSeach[2];
+ OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
+ OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+ OneItemSeach[0].uDataType = QCBOR_TYPE_ARRAY;
+ OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE;
+
+ QCBORDecode_Private_SearchAndGetArrayOrMap(pMe, OneItemSeach, pItem, pEncodedCBOR);
+}
+
+static inline void
+QCBORDecode_GetMap(QCBORDecodeContext *pMe,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR)
+{
+ QCBORDecode_Private_GetArrayOrMap(pMe, QCBOR_TYPE_MAP, pItem, pEncodedCBOR);
+}
+
+
+static inline void
+QCBORDecode_GetMapFromMapN(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR)
+{
+ QCBORItem OneItemSeach[2];
+ OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64;
+ OneItemSeach[0].label.int64 = nLabel;
+ OneItemSeach[0].uDataType = QCBOR_TYPE_MAP;
+ OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE;
+
+ QCBORDecode_Private_SearchAndGetArrayOrMap(pMe, OneItemSeach, pItem, pEncodedCBOR);
+}
+
+
+static inline void
+QCBORDecode_GetMapFromMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ QCBORItem *pItem,
+ UsefulBufC *pEncodedCBOR)
+{
+ QCBORItem OneItemSeach[2];
+ OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
+ OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel);
+ OneItemSeach[0].uDataType = QCBOR_TYPE_MAP;
+ OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE;
+
+ QCBORDecode_Private_SearchAndGetArrayOrMap(pMe, OneItemSeach, pItem, pEncodedCBOR);
+}
+
+
static inline void
QCBORDecode_GetInt64Convert(QCBORDecodeContext *pMe,