diff --git a/CMakeLists.txt b/CMakeLists.txt index c37cb38..adffeeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ target_sources(qcbor PRIVATE src/ieee754.c src/qcbor_decode.c + src/qcbor_spiffy_decode.c src/qcbor_tag_decode.c src/qcbor_number_decode.c src/qcbor_encode.c diff --git a/Makefile b/Makefile index 5f6d993..2f6c351 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ LIBS=-lm CFLAGS=$(CMD_LINE) -I inc -I test -Os -fPIC -QCBOR_OBJ=src/UsefulBuf.o src/qcbor_encode.o src/qcbor_decode.o src/qcbor_number_decode.o src/qcbor_tag_decode.o src/ieee754.o src/qcbor_err_to_str.o +QCBOR_OBJ=src/UsefulBuf.o src/qcbor_encode.o src/qcbor_decode.o src/qcbor_spiffy_decode.o src/qcbor_number_decode.o src/qcbor_tag_decode.o src/ieee754.o src/qcbor_err_to_str.o TEST_OBJ=test/UsefulBuf_Tests.o test/qcbor_encode_tests.o \ test/qcbor_decode_tests.o test/run_tests.o \ diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj index f9456a6..fb65bee 100644 --- a/QCBOR.xcodeproj/project.pbxproj +++ b/QCBOR.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 0FA9BEBA216DC7AD00BA646B /* qcbor_encode_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEB8216DC7AD00BA646B /* qcbor_encode_tests.c */; }; 0FA9BEBD216DE31700BA646B /* UsefulBuf_Tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9BEBC216DE31700BA646B /* UsefulBuf_Tests.c */; }; E7180F232CF1657B00513186 /* qcbor_number_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E7180F222CF1657A00513186 /* qcbor_number_decode.c */; }; + E7180F262CF8F2EB00513186 /* qcbor_spiffy_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E7180F252CF8F2EB00513186 /* qcbor_spiffy_decode.c */; }; E73B57592161CA690080D658 /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; }; E73B575E2161CA7C0080D658 /* float_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* float_tests.c */; }; E73B575F2161CA7C0080D658 /* half_to_double_from_rfc7049.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575D2161CA7C0080D658 /* half_to_double_from_rfc7049.c */; }; @@ -161,6 +162,8 @@ E7180F212CF1638F00513186 /* decode_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = decode_private.h; path = src/decode_private.h; sourceTree = ""; }; E7180F222CF1657A00513186 /* qcbor_number_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_number_decode.c; path = src/qcbor_number_decode.c; sourceTree = ""; }; E7180F242CF2390100513186 /* qcbor_number_decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_number_decode.h; path = inc/qcbor/qcbor_number_decode.h; sourceTree = ""; }; + E7180F252CF8F2EB00513186 /* qcbor_spiffy_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_spiffy_decode.c; path = src/qcbor_spiffy_decode.c; sourceTree = ""; }; + E7180F272CF917D200513186 /* decode_nesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = decode_nesting.h; path = src/decode_nesting.h; sourceTree = ""; }; E73B57572161CA680080D658 /* ieee754.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = ieee754.h; path = src/ieee754.h; sourceTree = ""; tabWidth = 3; }; E73B57582161CA690080D658 /* ieee754.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = ieee754.c; path = src/ieee754.c; sourceTree = ""; tabWidth = 3; }; E73B575A2161CA7C0080D658 /* float_tests.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = float_tests.c; path = test/float_tests.c; sourceTree = ""; tabWidth = 3; }; @@ -294,8 +297,10 @@ children = ( E776E08D214AE07500E67947 /* UsefulBuf.c */, E776E08C214AE07400E67947 /* qcbor_encode.c */, + E7180F272CF917D200513186 /* decode_nesting.h */, E7180F212CF1638F00513186 /* decode_private.h */, E776E08E214AE07500E67947 /* qcbor_decode.c */, + E7180F252CF8F2EB00513186 /* qcbor_spiffy_decode.c */, E7180F222CF1657A00513186 /* qcbor_number_decode.c */, E7CA1F1D2C8C337E0008F454 /* qcbor_tag_decode.c */, E73B57572161CA680080D658 /* ieee754.h */, @@ -564,6 +569,7 @@ E7C960B92800A09E00FB537C /* ub-example.c in Sources */, E743D0F324AD08020017899F /* example.c in Sources */, 0FA9BEBD216DE31700BA646B /* UsefulBuf_Tests.c in Sources */, + E7180F262CF8F2EB00513186 /* qcbor_spiffy_decode.c in Sources */, E7180F232CF1657B00513186 /* qcbor_number_decode.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/README.md b/README.md index d40430d..f51bebe 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,19 @@ maps and arrays without the caller having to do anything. This includes mixed definite and indefinte maps and arrays. (Some work remains to support map searching with indefinite length strings.) +## v2 + +** Tag decoding + +** Map Sorting + +** Serialization Modes + +** Files Rearrangement + +** Bignumber ?? + + ## Comparison to TinyCBOR TinyCBOR is a popular widely used implementation. Like QCBOR, diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h index 0f31e26..2f7a118 100644 --- a/inc/qcbor/qcbor_decode.h +++ b/inc/qcbor/qcbor_decode.h @@ -1214,137 +1214,6 @@ QCBORError QCBORDecode_EndCheck(QCBORDecodeContext *pCtx); -#ifndef QCBOR_DISABLE_TAGS -/** - * @brief Returns the tag numbers for an item. - * - * @param[in] pCtx The decoder context. - * @param[out] puTagNumber The returned tag number. - * - * In QCBOR v2, all tag numbers on an item MUST be fetched with this - * method. If not, @ref QCBOR_ERR_UNPROCESSED_TAG_NUMBER will - * occur. This is a major change from QCBORv1. The QCBOR v1 behavior - * is too lax for proper CBOR decoding. When a tag number occurs it - * indicates the item is a new data type (except for a few tag numbers - * that are hints). Note also that in RFC 7049, tag numbers were - * incorrectly characterized as optional implying they could be - * ignored. - * - * In typical item decoding, tag numbers are not used, not present and - * not expected. There's no need to call this. - * - * When the protocol being decoded does use a tag number then this - * must be called for the items were the tag numbers occur before the - * items themselves are decoded. Making this call prevents the - * @ref QCBOR_ERR_UNPROCESSED_TAG_NUMBER error, but the caller still has to - * check that the tag number is the right one. Probably the tag number - * will be used to switch the flow of the decoder. - * - * It's possible that an item might use the presence/absence of a tag - * number to switch the flow of decoding. If there's a possibility of - * a tag number then this must be called. - * - * If this is called and there is no tag number, then this will return - * @ref QCBOR_SUCCESS and the tag number returned will be - * @ref CBOR_TAG_INVALID64. - * - * Usually there is only one tag number per item, but CBOR allows - * more. That it allows nesting of tags where the content of one tag - * is another tag. If there are multiple tag numbers, this must be - * called multiple times. This only returns one tag number at a time, - * because tag numbers are typically processed one at a time. - * - * If there is an error decoding the tag or the item it is on, the - * error code will be set and the tag number @ref CBOR_TAG_INVALID64 - * will be returned. That is, @ref CBOR_TAG_INVALID64 will be returned if - * there is a decode error or there is no tag number. - */ -void -QCBORDecode_VGetNextTagNumber(QCBORDecodeContext *pCtx, uint64_t *puTagNumber); - - -/** - * @brief Returns the tag numbers for an item. - * - * @param[in] pCtx The decoder context. - * @param[out] puTagNumber The returned tag number. - * - * @return See error table of decoding errors set by QCBORDecode_VGetNext(). - * - * Like QCBORDecode_VGetNextTagNumber(), but returns the - * error rather than set last error. - */ -QCBORError -QCBORDecode_GetNextTagNumber(QCBORDecodeContext *pCtx, uint64_t *puTagNumber); - - -/** - * @brief Returns the tag numbers for a decoded item. - * - * @param[in] pCtx The decoder context. - * @param[in] pItem The CBOR item to get the tag for. - * @param[in] uIndex The index of the tag to get. - * - * @returns The nth tag number or @ref CBOR_TAG_INVALID64. - * - * Typically, this is only used with @ref QCBOR_DECODE_CONFIG_UNPROCESSED_TAG_NUMBERS. - * Normally, tag numbers are processed QCBORDecode_VGetNextTagNumber() or - * QCBORTagContentCallBack. - * - * When QCBOR decodes an item that is a tag, it will fully decode tags - * it is able to. Tags that it is unable to process are put in a list - * in the QCBORItem. - * - * Tags nest. Here the tag with index 0 is the outermost, the one - * furthest form the data item that is the tag content. This is - * the opposite order of QCBORDecode_GetNthTag(), but more - * useful. - * - * Deep tag nesting is rare so this implementation imposes a limit of - * @ref QCBOR_MAX_TAGS_PER_ITEM on nesting and returns @ref - * QCBOR_ERR_TOO_MANY_TAGS if there are more. This is a limit of this - * implementation, not of CBOR. (To be able to handle deeper nesting, - * the constant can be increased and the library recompiled. It will - * use more memory). - * - * See also @ref Tag-Decoding @ref CBORTags, @ref Tag-Usage and @ref Tags-Overview. - * - * To reduce memory used by a @ref QCBORItem, tag numbers larger than - * @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 - * getting the item. This is also returned if there are no tags on the - * item or no tag at @c uIndex. - */ -uint64_t -QCBORDecode_GetNthTagNumber(const QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint8_t uIndex); - - -/** - * @brief Returns the tag numbers for last-decoded item. - * - * @param[in] pCtx The decoder context. - * @param[in] uIndex The index of the tag to get. - * - * @returns The nth tag number or @ref CBOR_TAG_INVALID64. - * - * This returns tags of the most recently decoded item. See - * QCBORDecode_GetNthTagNumber(). This is particularly of use for spiffy - * decode functions that don't return a @ref QCBORItem. - * - * This does not work for QCBORDecode_GetNext(), - * QCBORDecode_PeekNext(), QCBORDecode_VPeekNext() or - * QCBORDecode_VGetNextConsume() but these all return a - * @ref QCBORItem, so it is not necessary. - * - * If a decoding error is set, then this returns @ref CBOR_TAG_INVALID64. - */ -uint64_t -QCBORDecode_GetNthTagNumberOfLast(QCBORDecodeContext *pCtx, uint8_t uIndex); - -#endif /* ! QCBOR_DISABLE_TAGS */ - /** * @brief Check that a decode completed successfully. @@ -1551,12 +1420,12 @@ QCBORDecode_SetError(QCBORDecodeContext *pCtx, QCBORError uError); * which causes no error to be returned when un processed tag numbers * are encountered. * - * Second, it installs all the same tag content handlers that v1 had hardwwired. + * Second, it installs all the same tag content handlers that were hardwired in v1. * QCBORDecode_InstallTagDecoders(pMe, QCBORDecode_TagDecoderTablev1, NULL); * * This is listed as deprecated even though it is new in QCBOR v2 * because it recommended that v1 mode not be used because the tag - * number processing is too loose. + * number processing is too loose. See @ref v2-Tag-Decoding. * * This links in a fair bit of object code for all the tag content * handlers that were always present in v1. To get the v1 behavior @@ -1568,43 +1437,6 @@ void QCBORDecode_CompatibilityV1(QCBORDecodeContext *pCtx); - -#ifndef QCBOR_DISABLE_TAGS - -/** - * @brief Returns the tag numbers for an item. (deprecated). - * - * @param[in] pCtx The decoder context. - * @param[in] uIndex The index of the tag to get. - * - * This is the same as QCBORDecode_GetNthTagNumber() but the order is - * opposite when there are multiple tags. @c uIndex 0 is the tag - * number closest to the tag content. QCBORDecode_GetNthTagNumber() is - * more useful for checking the next tag number and switching the - * decode flow. - */ -uint64_t -QCBORDecode_GetNthTag(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint32_t uIndex); - - -/** - * @brief Returns the tag numbers for last-decoded item (deprecated). - * - * @param[in] pCtx The decoder context. - * @param[in] uIndex The index of the tag to get. - * - * @returns The nth tag number or CBOR_TAG_INVALID64. - * - * This is the same as QCBORDecode_GetNthTagNumberOfLast() but the - * order is opposite when there are multiple tags. @c uIndex 0 is the - * tag number closest to the tag content. - * QCBORDecode_GetNthTagNumber() is more useful for checking - * the next tag number and switching the decode flow. - */ -uint64_t -QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pCtx, uint32_t uIndex); - -#endif /* ! QCBOR_DISABLE_TAGS */ /* ========================================================================= * * END OF DEPRECATED FUNCTIONS * * ========================================================================= */ diff --git a/inc/qcbor/qcbor_number_decode.h b/inc/qcbor/qcbor_number_decode.h index a995447..8c525ec 100644 --- a/inc/qcbor/qcbor_number_decode.h +++ b/inc/qcbor/qcbor_number_decode.h @@ -1401,7 +1401,7 @@ QCBORDecode_GetBigFloatBigInMapSZ(QCBORDecodeContext *pCtx, * ========================================================================= */ -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetUInt64Convert(QCBORDecodeContext *pCtx, uint32_t uConvertTypes, @@ -1409,7 +1409,7 @@ QCBORDecode_Private_GetUInt64Convert(QCBORDecodeContext *pCtx, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetUInt64ConvertInMapN(QCBORDecodeContext *pCtx, int64_t nLabel, @@ -1418,7 +1418,7 @@ QCBORDecode_Private_GetUInt64ConvertInMapN(QCBORDecodeContext *pCtx, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetUInt64ConvertInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, @@ -1426,14 +1426,14 @@ QCBORDecode_Private_GetUInt64ConvertInMapSZ(QCBORDecodeContext *pCtx, uint64_t *puValue, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetInt64Convert(QCBORDecodeContext *pCtx, uint32_t uConvertTypes, int64_t *pnValue, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetInt64ConvertInMapN(QCBORDecodeContext *pCtx, int64_t nLabel, @@ -1441,7 +1441,7 @@ QCBORDecode_Private_GetInt64ConvertInMapN(QCBORDecodeContext *pCtx, int64_t *pnValue, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetInt64ConvertInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, @@ -1451,14 +1451,14 @@ QCBORDecode_Private_GetInt64ConvertInMapSZ(QCBORDecodeContext *pCtx, #ifndef USEFULBUF_DISABLE_ALL_FLOAT -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetDoubleConvert(QCBORDecodeContext *pCtx, uint32_t uConvertTypes, double *pValue, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetDoubleConvertInMapN(QCBORDecodeContext *pCtx, int64_t nLabel, @@ -1466,7 +1466,7 @@ QCBORDecode_Private_GetDoubleConvertInMapN(QCBORDecodeContext *pCtx, double *pdValue, QCBORItem *pItem); -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_number_decode.c */ void QCBORDecode_Private_GetDoubleConvertInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, @@ -1476,7 +1476,7 @@ QCBORDecode_Private_GetDoubleConvertInMapSZ(QCBORDecodeContext *pCtx, #endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */ -/* Semi-private funcion used by public inline functions. See qcbor_decode.c */ +/* Semi-private funcion used by public inline functions. See qcbor_tag_decode.c */ void QCBORDecode_Private_GetTaggedString(QCBORDecodeContext *pMe, uint8_t uTagRequirement, @@ -1484,6 +1484,8 @@ QCBORDecode_Private_GetTaggedString(QCBORDecodeContext *pMe, uint64_t uTagNumber, UsefulBufC *pBstr); + +/* Semi-private funcion used by public inline functions. See qcbor_tag_decode.c */ void QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext *pMe, const int64_t nLabel, @@ -1493,11 +1495,7 @@ QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext *pMe, UsefulBufC *pString); - - - - - +/* Semi-private funcion used by public inline functions. See qcbor_tag_decode.c */ void QCBORDecode_Private_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, @@ -1650,7 +1648,7 @@ QCBORDecode_GetDoubleConvert(QCBORDecodeContext *pMe, double *pdValue) { QCBORItem Item; - QCBORDecode_Private_GetDoubleConvert(pMe, uConvertTypes, pdValue, &Item); + QCBORDecode_Private_GetDoubleConvert(pMe, uConvertTypes, pdValue, &Item); } static inline void @@ -1684,7 +1682,7 @@ QCBORDecode_GetDoubleConvertInMapSZ(QCBORDecodeContext *pMe, static inline void QCBORDecode_GetDouble(QCBORDecodeContext *pMe, double *pValue) { - QCBORDecode_GetDoubleConvert(pMe, QCBOR_CONVERT_TYPE_FLOAT, pValue); + QCBORDecode_GetDoubleConvert(pMe, QCBOR_CONVERT_TYPE_FLOAT, pValue); } static inline void @@ -1805,7 +1803,13 @@ QCBORDecode_GetDecimalFractionBigInMapN(QCBORDecodeContext *pMe, bool *pbMantissaIsNegative, int64_t *pnExponent) { - QCBORDecode_GetTDecimalFractionBigMantissaRawInMapN(pMe, nLabel, uTagRequirement, MantissaBuffer, pMantissa, pbMantissaIsNegative, pnExponent); + QCBORDecode_GetTDecimalFractionBigMantissaRawInMapN(pMe, + nLabel, + uTagRequirement, + MantissaBuffer, + pMantissa, + pbMantissaIsNegative, + pnExponent); } static inline void /* Deprecated */ diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h index 155df7c..26902d7 100644 --- a/inc/qcbor/qcbor_spiffy_decode.h +++ b/inc/qcbor/qcbor_spiffy_decode.h @@ -34,7 +34,9 @@ extern "C" { * * This section discusses spiffy decoding assuming familiarity with * the general description of decoding in the - * @ref BasicDecode section. + * @ref BasicDecode section. See also qcbor_tag_decode.h + * and qcbor_number_decode.h for more spiffy decode + * functions. * * Spiffy decode is extra decode features over and above the @ref * BasicDecode features that generally are easier to use, mirror the @@ -106,88 +108,11 @@ extern "C" { * quantified). One way ease this is to use * QCBORDecode_GetItemsInMap() which allows decoding of a list of * items expected in an map in one traveral. - * - * @anchor Tag-Usage - * ## Tag Usage - * - * Data types beyond the basic CBOR types of numbers, strings, maps and - * arrays are called tags. The main registry of these new types is in - * the IANA CBOR tags registry. These new types may be simple such a - * number that is to be interpreted as a date, or of moderate complexity - * such as defining a decimal fraction that is an array containing a - * mantissa and exponent, or complex such as format for signing and - * encryption. - * - * When a tag occurs in a protocol it is encoded as an integer tag - * number plus the content of the tag. - * - * The content format of a tag may also be "borrowed". For example, a - * protocol definition may say that a particular data item is an epoch - * date just like tag 1, but not actually tag 1. In practice the - * difference is the presence or absence of the integer tag number in - * the encoded CBOR. - * - * The decoding functions for these new types takes a tag requirement - * parameter to say whether the item is a tag, is just borrowing the - * content format and is not a tag, or whether either is OK. - * - * If the parameter indicates the item must be a tag (@ref - * QCBOR_TAG_REQUIREMENT_TAG), then @ref QCBOR_ERR_UNEXPECTED_TYPE is - * set if it is not one of the expected tag types. To decode correctly - * the contents of the tag must also be of the correct type. For - * example, to decode an epoch date tag the content must be an integer - * or floating-point value. - * - * If the parameter indicates it should not be a tag - * (@ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG), then - * @ref QCBOR_ERR_UNEXPECTED_TYPE set if it is a tag or the type of the - * encoded CBOR is not what is expected. In the example of an epoch - * date, the data type must be an integer or floating-point value. This - * is the case where the content format of a tag is borrowed. - * - * The parameter can also indicate that either a tag or no tag is - * allowed ( @ref QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG ). A good protocol - * design should however be clear and choose one or the other and not - * need this option. This is a way to implement "be liberal in what you - * accept", however these days that is less in favor. See - * https://tools.ietf.org/id/draft-thomson-postel-was-wrong-03.html. - * - * Map searching works with indefinite length strings. A string - * allocator must be set up the same as for any handling of indefinite - * length strings. However, It currently over-allocates memory from the - * string pool and thus requires a much larger string pool than it - * should. The over-allocation happens every time a map is searched by - * label. (This may be corrected in the future). */ -/** The data item must be a tag of the expected type. It is an error - * if it is not. For example when calling QCBORDecode_GetEpochDate(), - * the data item must be an @ref CBOR_TAG_DATE_EPOCH tag. See - * @ref Tag-Usage. */ -#define QCBOR_TAG_REQUIREMENT_TAG 0 - -/** The data item must be of the type expected for content data type - * being fetched. It is an error if it is not. For example, when - * calling QCBORDecode_GetEpochDate() and it must not be an @ref - * CBOR_TAG_DATE_EPOCH tag. See @ref Tag-Usage. */ -#define QCBOR_TAG_REQUIREMENT_NOT_A_TAG 1 - -/** Either of the above two are allowed. This allows implementation of - * being liberal in what you receive, but it is better if CBOR-based - * protocols pick one and stick to and not required the reciever to - * take either. See @ref Tag-Usage. */ -#define QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG 2 - -/** Add this into the above value if other tags not processed by QCBOR - * are to be allowed to surround the data item. See @ref Tag-Usage. */ -#define QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS 0x80 - - - - /** * @brief Decode the next item as a byte string * @@ -200,6 +125,8 @@ extern "C" { * * If the CBOR item to decode is not a byte string, the * @ref QCBOR_ERR_UNEXPECTED_TYPE error is set. + * + * See also QCBORDecode_EnterBstrWrapped(). */ static void QCBORDecode_GetByteString(QCBORDecodeContext *pCtx, @@ -746,13 +673,6 @@ QCBORDecode_GetItemsInMapWithCallback(QCBORDecodeContext *pCtx, void *pCallbackCtx, QCBORItemCallback pfCB); - -QCBORError -QCBORDecode_GetNextTagNumberInMapN(QCBORDecodeContext *pCtx, int64_t nLabel, uint64_t *puTagNumber); - -QCBORError -QCBORDecode_GetNextTagNumberInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, uint64_t *puTagNumber); - /** * @brief Decode the next item as a Boolean. * @@ -856,500 +776,6 @@ QCBORDecode_GetSimpleInMapSZ(QCBORDecodeContext *pCtx, -/** - * @brief Decode the next item as a date string. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pDateString The decoded date. - * - * This decodes the standard CBOR date/time string tag, integer tag - * number of 0, or encoded CBOR that is not a tag, but borrows the - * date string content format. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_DATE_STRING, QCBOREncode_AddDateString() and - * @ref QCBOR_TYPE_DATE_STRING. - */ -static void -QCBORDecode_GetDateString(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pDateString); - -static void -QCBORDecode_GetDateStringInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pDateString); - -static void -QCBORDecode_GetDateStringInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pDateString); - - -/** - * @brief Decode the next item as a date-only string. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pDateString The decoded date. - * - * This decodes the CBOR date-only string tag, integer tag number of - * 1004, or encoded CBOR that is not a tag, but borrows the date-only - * string content format. An example of the format is "1985-04-12". - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_DAYS_STRING, QCBOREncode_AddDaysString() and - * @ref QCBOR_TYPE_DAYS_STRING. - */ -static void -QCBORDecode_GetDaysString(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pDateString); - -static void -QCBORDecode_GetDaysStringInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pDateString); - -static void -QCBORDecode_GetDaysStringInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pDateString); - - -/** - * @brief Decode the next item as an epoch date. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pnTime The decoded epoch date. - * - * This decodes the standard CBOR epoch date/time tag, integer tag - * number of 1. This will also decode any integer or floating-point - * number as an epoch date (a tag 1 epoch date is just an integer or - * floating-point number). - * - * This will set @ref QCBOR_ERR_DATE_OVERFLOW if the input integer - * will not fit in an @c int64_t. Note that an @c int64_t can - * represent a range of over 500 billion years with one second - * resolution. - * - * Floating-point dates are always returned as an @c int64_t. The - * fractional part is discarded. - * - * If the input is a floating-point date and the QCBOR library is - * compiled with some or all floating-point features disabled, the - * following errors will be set. If the input is half-precision and - * half-precision is disabled @ref QCBOR_ERR_HALF_PRECISION_DISABLED - * is set. This function needs hardware floating-point to convert the - * floating-point value to an integer so if HW floating point is - * disabled @ref QCBOR_ERR_HW_FLOAT_DISABLED is set. If all - * floating-point is disabled then @ref QCBOR_ERR_ALL_FLOAT_DISABLED - * is set. A previous version of this function would return - * @ref QCBOR_ERR_FLOAT_DATE_DISABLED in some, but not all, cases when - * floating-point decoding was disabled. - * - * Floating-point dates that are plus infinity, minus infinity or NaN - * (not-a-number) will result in the @ref QCBOR_ERR_DATE_OVERFLOW - * error. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_DATE_EPOCH, QCBOREncode_AddTDateEpoch() and - * @ref QCBOR_TYPE_DATE_EPOCH. -*/ -void -QCBORDecode_GetEpochDate(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - int64_t *pnTime); - -void -QCBORDecode_GetEpochDateInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - int64_t *pnTime); - -void -QCBORDecode_GetEpochDateInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - int64_t *pnTime); - - -/** - * @brief Decode the next item as an days-count epoch date. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pnDays The decoded epoch date. - * - * This decodes the CBOR epoch date tag, integer tag number of 100, or - * encoded CBOR that is not a tag, but borrows the content format. The - * date is the number of days (not number of seconds) before or after - * Jan 1, 1970. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_DAYS_EPOCH, QCBOREncode_AddTDaysEpoch() and - * @ref QCBOR_TYPE_DAYS_EPOCH. -*/ -void -QCBORDecode_GetEpochDays(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - int64_t *pnDays); - -void -QCBORDecode_GetEpochDaysInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - int64_t *pnDays); - -void -QCBORDecode_GetEpochDaysInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - int64_t *pnDays); - - - - -/** - * @brief Decode the next item as a URI. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pURI The decoded URI. - * - * This decodes a standard CBOR URI tag, integer tag number of 32, or - * encoded CBOR that is not a tag, that is a URI encoded in a text - * string. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_URI, QCBOREncode_AddTURI() and - * @ref QCBOR_TYPE_URI. - */ -static void -QCBORDecode_GetURI(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pURI); - -static void -QCBORDecode_GetURIInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pURI); - -static void -QCBORDecode_GetURIInMapSZ(QCBORDecodeContext *pCtx, - const char * szLabel, - uint8_t uTagRequirement, - UsefulBufC *pURI); - - -/** - * @brief Decode the next item as base64 encoded text. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pB64Text The decoded base64 text. - * - * This decodes a standard CBOR base64 tag, integer tag number of 34, - * or encoded CBOR that is not a tag, that is base64 encoded bytes - * encoded in a text string. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * Note that this does not actually remove the base64 encoding. - * - * See also @ref CBOR_TAG_B64, QCBOREncode_AddB64Text() and - * @ref QCBOR_TYPE_BASE64. - */ -static void -QCBORDecode_GetB64(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pB64Text); - -static void -QCBORDecode_GetB64InMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pB64Text); - -static void -QCBORDecode_GetB64InMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pB64Text); - -/** - * @brief Decode the next item as base64URL encoded text. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pB64Text The decoded base64 text. - * - * This decodes a standard CBOR base64url tag, integer tag number of - * 33, or encoded CBOR that is not a tag, that is base64url encoded - * bytes encoded in a text string. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * Note that this does not actually remove the base64url encoding. - * - * See also @ref CBOR_TAG_B64URL, QCBOREncode_AddTB64URLText() and - * @ref QCBOR_TYPE_BASE64URL. - */ -static void -QCBORDecode_GetB64URL(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pB64Text); - -static void -QCBORDecode_GetB64URLInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pB64Text); - -static void -QCBORDecode_GetB64URLInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pB64Text); - -/** - * @brief Decode the next item as a regular expression. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pRegex The decoded regular expression. - * - * This decodes a standard CBOR regex tag, integer tag number of 35, - * or encoded CBOR that is not a tag, that is a PERL-compatible - * regular expression encoded in a text string. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_REGEX, QCBOREncode_AddTRegex() and - * @ref QCBOR_TYPE_REGEX. - */ -static void -QCBORDecode_GetRegex(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pRegex); - -static void -QCBORDecode_GetRegexInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pRegex); - -static void -QCBORDecode_GetRegexInMapSZ(QCBORDecodeContext *pCtx, - const char * szLabel, - uint8_t uTagRequirement, - UsefulBufC *pRegex); - - -/** - * @brief Decode the next item as a MIME message. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pMessage The decoded regular expression. - * @param[out] pbIsTag257 @c true if tag was 257. May be @c NULL. - * - * This decodes the standard CBOR MIME and binary MIME tags, integer - * tag numbers of 36 or 257, or encoded CBOR that is not a tag, that - * is a MIME message encoded in a text or binary string. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * The MIME message itself is not parsed. - * - * This decodes both tag 36 and 257. If it is tag 257, pbIsTag257 is - * @c true. The difference between the two is that tag 36 is utf8 and - * tag 257 is a byte string that can carry binary MIME. QCBOR - * processes them exactly the same. Possibly the difference can be - * ignored. NULL can be passed to have no value returned. - * - * See also @ref CBOR_TAG_MIME, @ref CBOR_TAG_BINARY_MIME, - * QCBOREncode_AddTMIMEData(), @ref QCBOR_TYPE_MIME and - * @ref QCBOR_TYPE_BINARY_MIME. - * - * This does no translation of line endings. See QCBOREncode_AddText() - * for a discussion of line endings in CBOR. - */ -void -QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pMessage, - bool *pbIsTag257); - - void -QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pMessage, - bool *pbIsTag257); - - -void -QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pMessage, - bool *pbIsTag257); - -/** - * @brief Decode the next item as a UUID. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pUUID The decoded UUID - * - * This decodes a standard CBOR UUID tag, integer tag number of 37, or - * encoded CBOR that is not a tag, that is a UUID encoded in a byte - * string. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See @ref Tag-Usage for discussion on tag requirements. - * - * See also @ref CBOR_TAG_BIN_UUID, QCBOREncode_AddTBinaryUUID() and - * @ref QCBOR_TYPE_UUID. - */ -static void -QCBORDecode_GetBinaryUUID(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pUUID); - -static void -QCBORDecode_GetBinaryUUIDInMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pUUID); - -static void -QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pUUID); - - - -/** - * @brief Decode some byte-string wrapped CBOR. - * - * @param[in] pCtx The decode context. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. - * @param[out] pBstr Pointer and length of byte-string wrapped CBOR (optional). - * - * This is for use on some CBOR that has been wrapped in a byte - * string. There are several ways that this can occur. - * - * First is tag 24 and tag 63. Tag 24 wraps a single CBOR data item - * and 63 a CBOR sequence. This implementation doesn't distinguish - * between the two (it would be more code and doesn't seem important). - * - * The @ref Tag-Usage discussion on the tag requirement applies here - * just the same as any other tag. - * - * In other cases, CBOR is wrapped in a byte string, but it is - * identified as CBOR by other means. The contents of a COSE payload - * are one example of that. They can be identified by the COSE content - * type, or they can be identified as CBOR indirectly by the protocol - * that uses COSE. for example, if a blob of CBOR is identified as a - * CWT, then the COSE payload is CBOR. To enter into CBOR of this - * type use the @ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG as the \c - * uTagRequirement argument. - * - * Note that byte string wrapped CBOR can also be decoded by getting - * the byte string with QCBORDecode_GetItem() or - * QCBORDecode_GetByteString() and feeding it into another instance of - * QCBORDecode. Doing it with this function has the advantage of using - * less memory as another instance of QCBORDecode is not necessary. - * - * When the wrapped CBOR is entered with this function, the pre-order - * traversal and such are bounded to the wrapped - * CBOR. QCBORDecode_ExitBstrWrapped() must be called to resume - * processing CBOR outside the wrapped CBOR. - * - * This does not work on indefinite-length strings. The - * error @ref QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING will be set. - * - * If @c pBstr is not @c NULL the pointer and length of the wrapped - * CBOR will be returned. This is usually not needed, but sometimes - * useful, particularly in the case of verifying signed data like the - * COSE payload. This is usually the pointer and length of the data is - * that is hashed or MACed. - * - * Please see @ref Decode-Errors-Overview "Decode Errors Overview". - * - * See also QCBORDecode_ExitBstrWrapped(), QCBORDecode_EnterMap() and - * QCBORDecode_EnterArray(). - */ -void -QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pCtx, - uint8_t uTagRequirement, - UsefulBufC *pBstr); - -void -QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pCtx, - int64_t nLabel, - uint8_t uTagRequirement, - UsefulBufC *pBstr); - -void -QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pCtx, - const char *szLabel, - uint8_t uTagRequirement, - UsefulBufC *pBstr); - - -/** - * @brief Exit some bstr-wrapped CBOR has been enetered. - * - * @param[in] pCtx The decode context. - * - * Bstr-wrapped CBOR must have been entered for this to succeed. - * - * The items in the wrapped CBOR that was entered do not have to have - * been consumed for this to succeed. - * - * The this sets the traversal cursor to the item after the - * byte string that was exited. - */ -void -QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pCtx); - - - /* ========================================================================= * @@ -1368,36 +794,20 @@ void QCBORDecode_Private_ExitBoundedMapOrArray(QCBORDecodeContext *pCtx, uint8_t uType); - /* Semi-private funcion used by public inline functions. See qcbor_decode.c */ void -QCBORDecode_Private_GetTaggedString(QCBORDecodeContext *pMe, - uint8_t uTagRequirement, - uint8_t uQCBOR_Type, - uint64_t uTagNumber, - UsefulBufC *pBstr); - -void -QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - const uint8_t uQCBOR_Type, - const uint64_t uTagNumber, - UsefulBufC *pString); - - - - - +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_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - uint8_t uTagRequirement, - uint8_t uQCBOR_Type, - uint64_t uTagNumber, - UsefulBufC *pString); +QCBORDecode_Private_SearchAndGetArrayOrMap(QCBORDecodeContext *pCtx, + QCBORItem *pTarget, + QCBORItem *pItem, + UsefulBufC *pEncodedCBOR); @@ -1425,22 +835,6 @@ QCBORDecode_ExitMap(QCBORDecodeContext *pMe) } -/* 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, @@ -1687,296 +1081,6 @@ QCBORDecode_GetUndefinedInMapSZ(QCBORDecodeContext *pMe, } - -static inline void -QCBORDecode_GetDateString(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pValue) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_DATE_STRING, - CBOR_TAG_DATE_STRING, - pValue); -} - -static inline void -QCBORDecode_GetDateStringInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pText) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_DATE_STRING, - CBOR_TAG_DATE_STRING, - pText); -} - -static inline void -QCBORDecode_GetDateStringInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pText) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_DATE_STRING, - CBOR_TAG_DATE_STRING, - pText); -} - -static inline void -QCBORDecode_GetDaysString(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pValue) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_DATE_STRING, - CBOR_TAG_DATE_STRING, - pValue); -} - -static inline void -QCBORDecode_GetDaysStringInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pText) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_DAYS_STRING, - CBOR_TAG_DAYS_STRING, - pText); -} - - -static inline void -QCBORDecode_GetDaysStringInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pText) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_DAYS_STRING, - CBOR_TAG_DAYS_STRING, - pText); - -} - - - -static inline void -QCBORDecode_GetURI(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pUUID) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_URI, - CBOR_TAG_URI, - pUUID); -} - -static inline void -QCBORDecode_GetURIInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pUUID) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_URI, - CBOR_TAG_URI, - pUUID); -} - -static inline void -QCBORDecode_GetURIInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pUUID) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_URI, - CBOR_TAG_URI, - pUUID); -} - - -static inline void -QCBORDecode_GetB64(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pB64Text) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_BASE64, - CBOR_TAG_B64, - pB64Text); -} - -static inline void -QCBORDecode_GetB64InMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pB64Text) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_BASE64, - CBOR_TAG_B64, - pB64Text); -} - -static inline void -QCBORDecode_GetB64InMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pB64Text) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_BASE64, - CBOR_TAG_B64, - pB64Text); -} - - -static inline void -QCBORDecode_GetB64URL(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pB64Text) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_BASE64URL, - CBOR_TAG_B64URL, - pB64Text); -} - -static inline void -QCBORDecode_GetB64URLInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pB64Text) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_BASE64URL, - CBOR_TAG_B64URL, - pB64Text); -} - -static inline void -QCBORDecode_GetB64URLInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pB64Text) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_BASE64URL, - CBOR_TAG_B64URL, - pB64Text); -} - - -static inline void -QCBORDecode_GetRegex(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pRegex) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_REGEX, - CBOR_TAG_REGEX, - pRegex); -} - -static inline void -QCBORDecode_GetRegexInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pRegex) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_REGEX, - CBOR_TAG_REGEX, - pRegex); -} - - -static inline void -QCBORDecode_GetRegexInMapSZ(QCBORDecodeContext *pMe, - const char * szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pRegex) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_REGEX, - CBOR_TAG_REGEX, - pRegex); -} - - -static inline void -QCBORDecode_GetBinaryUUID(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pUUID) -{ - QCBORDecode_Private_GetTaggedString(pMe, - uTagRequirement, - QCBOR_TYPE_UUID, - CBOR_TAG_BIN_UUID, - pUUID); -} - -static inline void -QCBORDecode_GetBinaryUUIDInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pUUID) -{ - QCBORDecode_Private_GetTaggedStringInMapN(pMe, - nLabel, - uTagRequirement, - QCBOR_TYPE_UUID, - CBOR_TAG_BIN_UUID, - pUUID); -} - -static inline void -QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pUUID) -{ - QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, - szLabel, - uTagRequirement, - QCBOR_TYPE_UUID, - CBOR_TAG_BIN_UUID, - pUUID); -} - /* ======================================================================== * * END OF PRIVATE INLINE IMPLEMENTATION * * ======================================================================== */ diff --git a/inc/qcbor/qcbor_tag_decode.h b/inc/qcbor/qcbor_tag_decode.h index d952387..7f92a48 100644 --- a/inc/qcbor/qcbor_tag_decode.h +++ b/inc/qcbor/qcbor_tag_decode.h @@ -1,5 +1,5 @@ /* ========================================================================== - * qcbor_tag_decode.h -- Tag content decoders + * qcbor_tag_decode.h -- Tag decoding * * Copyright (c) 2024, Laurence Lundblade. All rights reserved. * @@ -7,7 +7,7 @@ * * See BSD-3-Clause license in README.md * - * Created on 9/5/24 + * Forked from qcbor_decode.c on 9/5/24 * ========================================================================== */ #ifndef qcbor_tag_decode_h @@ -42,34 +42,742 @@ extern "C" { * ## Tags Decoding * * TODO: lots to write here + * + * * @anchor Tag-Usage + * ## Tag Usage + * + * Data types beyond the basic CBOR types of numbers, strings, maps and + * arrays are called tags. The main registry of these new types is in + * the IANA CBOR tags registry. These new types may be simple such a + * number that is to be interpreted as a date, or of moderate complexity + * such as defining a decimal fraction that is an array containing a + * mantissa and exponent, or complex such as format for signing and + * encryption. + * + * When a tag occurs in a protocol it is encoded as an integer tag + * number plus the content of the tag. + * + * The content format of a tag may also be "borrowed". For example, a + * protocol definition may say that a particular data item is an epoch + * date just like tag 1, but not actually tag 1. In practice the + * difference is the presence or absence of the integer tag number in + * the encoded CBOR. + * + * The decoding functions for these new types takes a tag requirement + * parameter to say whether the item is a tag, is just borrowing the + * content format and is not a tag, or whether either is OK. + * + * If the parameter indicates the item must be a tag (@ref + * QCBOR_TAG_REQUIREMENT_TAG), then @ref QCBOR_ERR_UNEXPECTED_TYPE is + * set if it is not one of the expected tag types. To decode correctly + * the contents of the tag must also be of the correct type. For + * example, to decode an epoch date tag the content must be an integer + * or floating-point value. + * + * If the parameter indicates it should not be a tag + * (@ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG), then + * @ref QCBOR_ERR_UNEXPECTED_TYPE set if it is a tag or the type of the + * encoded CBOR is not what is expected. In the example of an epoch + * date, the data type must be an integer or floating-point value. This + * is the case where the content format of a tag is borrowed. + * + * The parameter can also indicate that either a tag or no tag is + * allowed ( @ref QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG ). A good protocol + * design should however be clear and choose one or the other and not + * need this option. This is a way to implement "be liberal in what you + * accept", however these days that is less in favor. See + * https://tools.ietf.org/id/draft-thomson-postel-was-wrong-03.html. + * + * Map searching works with indefinite length strings. A string + * allocator must be set up the same as for any handling of indefinite + * length strings. However, It currently over-allocates memory from the + * string pool and thus requires a much larger string pool than it + * should. The over-allocation happens every time a map is searched by + * label. (This may be corrected in the future). + * + * + * TODO: this text isn't right + * In v1, some spiffy decode functions ignored tag numbers and + * some didn't. For example, GetInt64 ignored and GetString didn't. + * The "GetXxx" where Xxxx is a tag ignore conditionally based + * on an argument. + * (Would be good to verify this with tests) + * + * Do we fix the behavior of GetString in v1? Relax so it + * allows tag numbers like the rest? Probably. + * + * In v2, the whole mechanism is with GetTagNumbers. They are + * never ignored and they must always be consumed. + * + * With v2 in v1 mode, the functions that were ignoring + * tags must go back to ignoring them. + * + * How does TagRequirement work in v2? + * + * GetInt64 and GetString require all tag numbs to be processed + * to work. */ -/* - TODO: integrate this - In v1, some spiffy decode functions ignored tag numbers and - some didn't. For example, GetInt64 ignored and GetString didn't. - The "GetXxx" where Xxxx is a tag ignore conditionally based - on an argument. - (Would be good to verify this with tests) +/** The data item must be a tag of the expected type. It is an error + * if it is not. For example when calling QCBORDecode_GetEpochDate(), + * the data item must be an @ref CBOR_TAG_DATE_EPOCH tag. See + * @ref Tag-Usage. */ +#define QCBOR_TAG_REQUIREMENT_TAG 0 - Do we fix the behavior of GetString in v1? Relax so it - allows tag numbers like the rest? Probably. +/** The data item must be of the type expected for content data type + * being fetched. It is an error if it is not. For example, when + * calling QCBORDecode_GetEpochDate() and it must not be an @ref + * CBOR_TAG_DATE_EPOCH tag. See @ref Tag-Usage. */ +#define QCBOR_TAG_REQUIREMENT_NOT_A_TAG 1 - In v2, the whole mechanism is with GetTagNumbers. They are - never ignored and they must always be consumed. +/** Either of the above two are allowed. This allows implementation of + * being liberal in what you receive, but it is better if CBOR-based + * protocols pick one and stick to and not required the reciever to + * take either. See @ref Tag-Usage. */ +#define QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG 2 - With v2 in v1 mode, the functions that were ignoring - tags must go back to ignoring them. +/** Add this into the above value if other tags not processed by QCBOR + * are to be allowed to surround the data item. See @ref Tag-Usage. */ +#define QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS 0x80 - How does TagRequirement work in v2? - GetInt64 and GetString require all tag numbs to be processed - to work. +#ifndef QCBOR_DISABLE_TAGS +/** + * @brief Returns the tag numbers for an item. + * + * @param[in] pCtx The decoder context. + * @param[out] puTagNumber The returned tag number. + * + * In QCBOR v2, all tag numbers on an item MUST be fetched with this + * method. If not, @ref QCBOR_ERR_UNPROCESSED_TAG_NUMBER will + * occur. This is a major change from QCBORv1. The QCBOR v1 behavior + * is too lax for proper CBOR decoding. When a tag number occurs it + * indicates the item is a new data type (except for a few tag numbers + * that are hints). Note also that in RFC 7049, tag numbers were + * incorrectly characterized as optional implying they could be + * ignored. + * + * In typical item decoding, tag numbers are not used, not present and + * not expected. There's no need to call this. + * + * When the protocol being decoded does use a tag number then this + * must be called for the items were the tag numbers occur before the + * items themselves are decoded. Making this call prevents the + * @ref QCBOR_ERR_UNPROCESSED_TAG_NUMBER error, but the caller still has to + * check that the tag number is the right one. Probably the tag number + * will be used to switch the flow of the decoder. + * + * It's possible that an item might use the presence/absence of a tag + * number to switch the flow of decoding. If there's a possibility of + * a tag number then this must be called. + * + * If this is called and there is no tag number, then this will return + * @ref QCBOR_SUCCESS and the tag number returned will be + * @ref CBOR_TAG_INVALID64. + * + * Usually there is only one tag number per item, but CBOR allows + * more. That it allows nesting of tags where the content of one tag + * is another tag. If there are multiple tag numbers, this must be + * called multiple times. This only returns one tag number at a time, + * because tag numbers are typically processed one at a time. + * + * If there is an error decoding the tag or the item it is on, the + * error code will be set and the tag number @ref CBOR_TAG_INVALID64 + * will be returned. That is, @ref CBOR_TAG_INVALID64 will be returned if + * there is a decode error or there is no tag number. + */ +void +QCBORDecode_VGetNextTagNumber(QCBORDecodeContext *pCtx, uint64_t *puTagNumber); +/** + * @brief Returns the tag numbers for an item. + * + * @param[in] pCtx The decoder context. + * @param[out] puTagNumber The returned tag number. + * + * @return See error table of decoding errors set by QCBORDecode_VGetNext(). + * + * Like QCBORDecode_VGetNextTagNumber(), but returns the + * error rather than set last error. */ +QCBORError +QCBORDecode_GetNextTagNumber(QCBORDecodeContext *pCtx, uint64_t *puTagNumber); + +QCBORError +QCBORDecode_GetNextTagNumberInMapN(QCBORDecodeContext *pCtx, int64_t nLabel, uint64_t *puTagNumber); + + +QCBORError +QCBORDecode_GetNextTagNumberInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, uint64_t *puTagNumber); + + + +/** + * @brief Returns the tag numbers for a decoded item. + * + * @param[in] pCtx The decoder context. + * @param[in] pItem The CBOR item to get the tag for. + * @param[in] uIndex The index of the tag to get. + * + * @returns The nth tag number or @ref CBOR_TAG_INVALID64. + * + * Typically, this is only used with @ref QCBOR_DECODE_CONFIG_UNPROCESSED_TAG_NUMBERS. + * Normally, tag numbers are processed QCBORDecode_VGetNextTagNumber() or + * QCBORTagContentCallBack. + * + * When QCBOR decodes an item that is a tag, it will fully decode tags + * it is able to. Tags that it is unable to process are put in a list + * in the QCBORItem. + * + * Tags nest. Here the tag with index 0 is the outermost, the one + * furthest form the data item that is the tag content. This is + * the opposite order of QCBORDecode_GetNthTag(), but more + * useful. + * + * Deep tag nesting is rare so this implementation imposes a limit of + * @ref QCBOR_MAX_TAGS_PER_ITEM on nesting and returns @ref + * QCBOR_ERR_TOO_MANY_TAGS if there are more. This is a limit of this + * implementation, not of CBOR. (To be able to handle deeper nesting, + * the constant can be increased and the library recompiled. It will + * use more memory). + * + * See also @ref Tag-Decoding @ref CBORTags, @ref Tag-Usage and @ref Tags-Overview. + * + * To reduce memory used by a @ref QCBORItem, tag numbers larger than + * @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 + * getting the item. This is also returned if there are no tags on the + * item or no tag at @c uIndex. + */ +uint64_t +QCBORDecode_GetNthTagNumber(const QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint8_t uIndex); + + +/** + * @brief Returns the tag numbers for last-decoded item. + * + * @param[in] pCtx The decoder context. + * @param[in] uIndex The index of the tag to get. + * + * @returns The nth tag number or @ref CBOR_TAG_INVALID64. + * + * This returns tags of the most recently decoded item. See + * QCBORDecode_GetNthTagNumber(). This is particularly of use for spiffy + * decode functions that don't return a @ref QCBORItem. + * + * This does not work for QCBORDecode_GetNext(), + * QCBORDecode_PeekNext(), QCBORDecode_VPeekNext() or + * QCBORDecode_VGetNextConsume() but these all return a + * @ref QCBORItem, so it is not necessary. + * + * If a decoding error is set, then this returns @ref CBOR_TAG_INVALID64. + */ +uint64_t +QCBORDecode_GetNthTagNumberOfLast(QCBORDecodeContext *pCtx, uint8_t uIndex); + +#endif /* ! QCBOR_DISABLE_TAGS */ + + + + +/** + * @brief Decode some byte-string wrapped CBOR. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pBstr Pointer and length of byte-string wrapped CBOR (optional). + * + * This is for use on some CBOR that has been wrapped in a byte + * string. There are several ways that this can occur. + * + * First is tag 24 and tag 63. Tag 24 wraps a single CBOR data item + * and 63 a CBOR sequence. This implementation doesn't distinguish + * between the two (it would be more code and doesn't seem important). + * + * The @ref Tag-Usage discussion on the tag requirement applies here + * just the same as any other tag. + * + * In other cases, CBOR is wrapped in a byte string, but it is + * identified as CBOR by other means. The contents of a COSE payload + * are one example of that. They can be identified by the COSE content + * type, or they can be identified as CBOR indirectly by the protocol + * that uses COSE. for example, if a blob of CBOR is identified as a + * CWT, then the COSE payload is CBOR. To enter into CBOR of this + * type use the @ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG as the \c + * uTagRequirement argument. + * + * Note that byte string wrapped CBOR can also be decoded by getting + * the byte string with QCBORDecode_GetItem() or + * QCBORDecode_GetByteString() and feeding it into another instance of + * QCBORDecode. Doing it with this function has the advantage of using + * less memory as another instance of QCBORDecode is not necessary. + * + * When the wrapped CBOR is entered with this function, the pre-order + * traversal and such are bounded to the wrapped + * CBOR. QCBORDecode_ExitBstrWrapped() must be called to resume + * processing CBOR outside the wrapped CBOR. + * + * This does not work on indefinite-length strings. The + * error @ref QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING will be set. + * + * If @c pBstr is not @c NULL the pointer and length of the wrapped + * CBOR will be returned. This is usually not needed, but sometimes + * useful, particularly in the case of verifying signed data like the + * COSE payload. This is usually the pointer and length of the data is + * that is hashed or MACed. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See also QCBORDecode_ExitBstrWrapped(), QCBORDecode_EnterMap() and + * QCBORDecode_EnterArray(). + */ +void +QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pBstr); + +void +QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pBstr); + +void +QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pBstr); + + +/** + * @brief Exit some bstr-wrapped CBOR has been enetered. + * + * @param[in] pCtx The decode context. + * + * Bstr-wrapped CBOR must have been entered for this to succeed. + * + * The items in the wrapped CBOR that was entered do not have to have + * been consumed for this to succeed. + * + * The this sets the traversal cursor to the item after the + * byte string that was exited. + */ +void +QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pCtx); + + + + +/** + * @brief Decode the next item as a date string. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pDateString The decoded date. + * + * This decodes the standard CBOR date/time string tag, integer tag + * number of 0, or encoded CBOR that is not a tag, but borrows the + * date string content format. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_DATE_STRING, QCBOREncode_AddDateString() and + * @ref QCBOR_TYPE_DATE_STRING. + */ +static void +QCBORDecode_GetDateString(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pDateString); + +static void +QCBORDecode_GetDateStringInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pDateString); + +static void +QCBORDecode_GetDateStringInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pDateString); + + +/** + * @brief Decode the next item as an epoch date. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pnTime The decoded epoch date. + * + * This decodes the standard CBOR epoch date/time tag, integer tag + * number of 1. This will also decode any integer or floating-point + * number as an epoch date (a tag 1 epoch date is just an integer or + * floating-point number). + * + * This will set @ref QCBOR_ERR_DATE_OVERFLOW if the input integer + * will not fit in an @c int64_t. Note that an @c int64_t can + * represent a range of over 500 billion years with one second + * resolution. + * + * Floating-point dates are always returned as an @c int64_t. The + * fractional part is discarded. + * + * If the input is a floating-point date and the QCBOR library is + * compiled with some or all floating-point features disabled, the + * following errors will be set. If the input is half-precision and + * half-precision is disabled @ref QCBOR_ERR_HALF_PRECISION_DISABLED + * is set. This function needs hardware floating-point to convert the + * floating-point value to an integer so if HW floating point is + * disabled @ref QCBOR_ERR_HW_FLOAT_DISABLED is set. If all + * floating-point is disabled then @ref QCBOR_ERR_ALL_FLOAT_DISABLED + * is set. A previous version of this function would return + * @ref QCBOR_ERR_FLOAT_DATE_DISABLED in some, but not all, cases when + * floating-point decoding was disabled. + * + * Floating-point dates that are plus infinity, minus infinity or NaN + * (not-a-number) will result in the @ref QCBOR_ERR_DATE_OVERFLOW + * error. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_DATE_EPOCH, QCBOREncode_AddTDateEpoch() and + * @ref QCBOR_TYPE_DATE_EPOCH. +*/ +void +QCBORDecode_GetEpochDate(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + int64_t *pnTime); + +void +QCBORDecode_GetEpochDateInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + int64_t *pnTime); + +void +QCBORDecode_GetEpochDateInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + int64_t *pnTime); + + + +/** + * @brief Decode the next item as a date-only string. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pDateString The decoded date. + * + * This decodes the CBOR date-only string tag, integer tag number of + * 1004, or encoded CBOR that is not a tag, but borrows the date-only + * string content format. An example of the format is "1985-04-12". + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_DAYS_STRING, QCBOREncode_AddDaysString() and + * @ref QCBOR_TYPE_DAYS_STRING. + */ +static void +QCBORDecode_GetDaysString(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pDateString); + +static void +QCBORDecode_GetDaysStringInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pDateString); + +static void +QCBORDecode_GetDaysStringInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pDateString); + + +/** + * @brief Decode the next item as an days-count epoch date. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pnDays The decoded epoch date. + * + * This decodes the CBOR epoch date tag, integer tag number of 100, or + * encoded CBOR that is not a tag, but borrows the content format. The + * date is the number of days (not number of seconds) before or after + * Jan 1, 1970. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_DAYS_EPOCH, QCBOREncode_AddTDaysEpoch() and + * @ref QCBOR_TYPE_DAYS_EPOCH. +*/ +void +QCBORDecode_GetEpochDays(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + int64_t *pnDays); + +void +QCBORDecode_GetEpochDaysInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + int64_t *pnDays); + +void +QCBORDecode_GetEpochDaysInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + int64_t *pnDays); + + +/** + * @brief Decode the next item as a URI. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pURI The decoded URI. + * + * This decodes a standard CBOR URI tag, integer tag number of 32, or + * encoded CBOR that is not a tag, that is a URI encoded in a text + * string. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_URI, QCBOREncode_AddTURI() and + * @ref QCBOR_TYPE_URI. + */ +static void +QCBORDecode_GetURI(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pURI); + +static void +QCBORDecode_GetURIInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pURI); + +static void +QCBORDecode_GetURIInMapSZ(QCBORDecodeContext *pCtx, + const char * szLabel, + uint8_t uTagRequirement, + UsefulBufC *pURI); + + +/** + * @brief Decode the next item as base64 encoded text. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pB64Text The decoded base64 text. + * + * This decodes a standard CBOR base64 tag, integer tag number of 34, + * or encoded CBOR that is not a tag, that is base64 encoded bytes + * encoded in a text string. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * Note that this does not actually remove the base64 encoding. + * + * See also @ref CBOR_TAG_B64, QCBOREncode_AddB64Text() and + * @ref QCBOR_TYPE_BASE64. + */ +static void +QCBORDecode_GetB64(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pB64Text); + +static void +QCBORDecode_GetB64InMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pB64Text); + +static void +QCBORDecode_GetB64InMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pB64Text); + +/** + * @brief Decode the next item as base64URL encoded text. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pB64Text The decoded base64 text. + * + * This decodes a standard CBOR base64url tag, integer tag number of + * 33, or encoded CBOR that is not a tag, that is base64url encoded + * bytes encoded in a text string. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * Note that this does not actually remove the base64url encoding. + * + * See also @ref CBOR_TAG_B64URL, QCBOREncode_AddTB64URLText() and + * @ref QCBOR_TYPE_BASE64URL. + */ +static void +QCBORDecode_GetB64URL(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pB64Text); + +static void +QCBORDecode_GetB64URLInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pB64Text); + +static void +QCBORDecode_GetB64URLInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pB64Text); + +/** + * @brief Decode the next item as a regular expression. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pRegex The decoded regular expression. + * + * This decodes a standard CBOR regex tag, integer tag number of 35, + * or encoded CBOR that is not a tag, that is a PERL-compatible + * regular expression encoded in a text string. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_REGEX, QCBOREncode_AddTRegex() and + * @ref QCBOR_TYPE_REGEX. + */ +static void +QCBORDecode_GetRegex(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pRegex); + +static void +QCBORDecode_GetRegexInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pRegex); + +static void +QCBORDecode_GetRegexInMapSZ(QCBORDecodeContext *pCtx, + const char * szLabel, + uint8_t uTagRequirement, + UsefulBufC *pRegex); + + +/** + * @brief Decode the next item as a MIME message. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pMessage The decoded regular expression. + * @param[out] pbIsTag257 @c true if tag was 257. May be @c NULL. + * + * This decodes the standard CBOR MIME and binary MIME tags, integer + * tag numbers of 36 or 257, or encoded CBOR that is not a tag, that + * is a MIME message encoded in a text or binary string. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * The MIME message itself is not parsed. + * + * This decodes both tag 36 and 257. If it is tag 257, pbIsTag257 is + * @c true. The difference between the two is that tag 36 is utf8 and + * tag 257 is a byte string that can carry binary MIME. QCBOR + * processes them exactly the same. Possibly the difference can be + * ignored. NULL can be passed to have no value returned. + * + * See also @ref CBOR_TAG_MIME, @ref CBOR_TAG_BINARY_MIME, + * QCBOREncode_AddTMIMEData(), @ref QCBOR_TYPE_MIME and + * @ref QCBOR_TYPE_BINARY_MIME. + * + * This does no translation of line endings. See QCBOREncode_AddText() + * for a discussion of line endings in CBOR. + */ +void +QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pMessage, + bool *pbIsTag257); + + void +QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pMessage, + bool *pbIsTag257); + + +void +QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pMessage, + bool *pbIsTag257); + +/** + * @brief Decode the next item as a UUID. + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX. + * @param[out] pUUID The decoded UUID + * + * This decodes a standard CBOR UUID tag, integer tag number of 37, or + * encoded CBOR that is not a tag, that is a UUID encoded in a byte + * string. + * + * Please see @ref Decode-Errors-Overview "Decode Errors Overview". + * + * See @ref Tag-Usage for discussion on tag requirements. + * + * See also @ref CBOR_TAG_BIN_UUID, QCBOREncode_AddTBinaryUUID() and + * @ref QCBOR_TYPE_UUID. + */ +static void +QCBORDecode_GetBinaryUUID(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + UsefulBufC *pUUID); + +static void +QCBORDecode_GetBinaryUUIDInMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + UsefulBufC *pUUID); + +static void +QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + UsefulBufC *pUUID); + + /** @@ -354,10 +1062,89 @@ QCBORDecode_ExpMantissaTagCB(QCBORDecodeContext *pDecodeCtx, +/* ========================================================================= * + * BEGINNING OF DEPRECATED FUNCTIONS * + * * + * There is no plan to remove these in future versions. * + * They just have been replaced by something better. * + * ========================================================================= */ + +#ifndef QCBOR_DISABLE_TAGS + +/** + * @brief Returns the tag numbers for an item. (deprecated). + * + * @param[in] pCtx The decoder context. + * @param[in] uIndex The index of the tag to get. + * + * This is the same as QCBORDecode_GetNthTagNumber() but the order is + * opposite when there are multiple tags. @c uIndex 0 is the tag + * number closest to the tag content. QCBORDecode_GetNthTagNumber() is + * more useful for checking the next tag number and switching the + * decode flow. + */ +uint64_t +QCBORDecode_GetNthTag(QCBORDecodeContext *pCtx, const QCBORItem *pItem, uint32_t uIndex); + + +/** + * @brief Returns the tag numbers for last-decoded item (deprecated). + * + * @param[in] pCtx The decoder context. + * @param[in] uIndex The index of the tag to get. + * + * @returns The nth tag number or CBOR_TAG_INVALID64. + * + * This is the same as QCBORDecode_GetNthTagNumberOfLast() but the + * order is opposite when there are multiple tags. @c uIndex 0 is the + * tag number closest to the tag content. + * QCBORDecode_GetNthTagNumber() is more useful for checking + * the next tag number and switching the decode flow. + */ +uint64_t +QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pCtx, uint32_t uIndex); + +#endif /* ! QCBOR_DISABLE_TAGS */ + + +/* ========================================================================= * + * END OF DEPRECATED FUNCTIONS * + * ========================================================================= */ + + + /* ========================================================================= * * BEGINNING OF PRIVATE INLINE IMPLEMENTATION * * ========================================================================= */ +/* Semi-private used by public inline functions. See qcbor_tag_decode.c */ +void +QCBORDecode_Private_GetTaggedString(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + uint8_t uQCBOR_Type, + uint64_t uTagNumber, + UsefulBufC *pBstr); + +/* Semi-private used by public inline functions. See qcbor_tag_decode.c */ +void +QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + const uint8_t uQCBOR_Type, + const uint64_t uTagNumber, + UsefulBufC *pString); + +/* Semi-private used by public inline functions. See qcbor_tag_decode.c */ +void +QCBORDecode_Private_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + uint8_t uQCBOR_Type, + uint64_t uTagNumber, + UsefulBufC *pString); + + + #ifndef QCBOR_DISABLE_TAGS static inline void @@ -372,6 +1159,295 @@ QCBORDecode_InstallTagDecoders(QCBORDecodeContext *pMe, #endif /* ! QCBOR_DISABLE_TAGS */ + + +static inline void +QCBORDecode_GetDateString(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pValue) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_DATE_STRING, + CBOR_TAG_DATE_STRING, + pValue); +} + +static inline void +QCBORDecode_GetDateStringInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pText) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_DATE_STRING, + CBOR_TAG_DATE_STRING, + pText); +} + +static inline void +QCBORDecode_GetDateStringInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pText) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_DATE_STRING, + CBOR_TAG_DATE_STRING, + pText); +} + +static inline void +QCBORDecode_GetDaysString(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pValue) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_DATE_STRING, + CBOR_TAG_DATE_STRING, + pValue); +} + +static inline void +QCBORDecode_GetDaysStringInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pText) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_DAYS_STRING, + CBOR_TAG_DAYS_STRING, + pText); +} + +static inline void +QCBORDecode_GetDaysStringInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pText) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_DAYS_STRING, + CBOR_TAG_DAYS_STRING, + pText); + +} + + +static inline void +QCBORDecode_GetURI(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pUUID) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_URI, + CBOR_TAG_URI, + pUUID); +} + +static inline void +QCBORDecode_GetURIInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pUUID) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_URI, + CBOR_TAG_URI, + pUUID); +} + +static inline void +QCBORDecode_GetURIInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pUUID) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_URI, + CBOR_TAG_URI, + pUUID); +} + + +static inline void +QCBORDecode_GetB64(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pB64Text) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_BASE64, + CBOR_TAG_B64, + pB64Text); +} + +static inline void +QCBORDecode_GetB64InMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pB64Text) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_BASE64, + CBOR_TAG_B64, + pB64Text); +} + +static inline void +QCBORDecode_GetB64InMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pB64Text) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_BASE64, + CBOR_TAG_B64, + pB64Text); +} + + +static inline void +QCBORDecode_GetB64URL(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pB64Text) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_BASE64URL, + CBOR_TAG_B64URL, + pB64Text); +} + +static inline void +QCBORDecode_GetB64URLInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pB64Text) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_BASE64URL, + CBOR_TAG_B64URL, + pB64Text); +} + +static inline void +QCBORDecode_GetB64URLInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pB64Text) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_BASE64URL, + CBOR_TAG_B64URL, + pB64Text); +} + + +static inline void +QCBORDecode_GetRegex(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pRegex) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_REGEX, + CBOR_TAG_REGEX, + pRegex); +} + +static inline void +QCBORDecode_GetRegexInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pRegex) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_REGEX, + CBOR_TAG_REGEX, + pRegex); +} + +static inline void +QCBORDecode_GetRegexInMapSZ(QCBORDecodeContext *pMe, + const char * szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pRegex) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_REGEX, + CBOR_TAG_REGEX, + pRegex); +} + + +static inline void +QCBORDecode_GetBinaryUUID(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pUUID) +{ + QCBORDecode_Private_GetTaggedString(pMe, + uTagRequirement, + QCBOR_TYPE_UUID, + CBOR_TAG_BIN_UUID, + pUUID); +} + +static inline void +QCBORDecode_GetBinaryUUIDInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pUUID) +{ + QCBORDecode_Private_GetTaggedStringInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_UUID, + CBOR_TAG_BIN_UUID, + pUUID); +} + +static inline void +QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pUUID) +{ + QCBORDecode_Private_GetTaggedStringInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_UUID, + CBOR_TAG_BIN_UUID, + pUUID); +} + + /* ======================================================================== * * END OF PRIVATE INLINE IMPLEMENTATION * * ======================================================================== */ diff --git a/src/decode_nesting.h b/src/decode_nesting.h new file mode 100644 index 0000000..0e1c3a1 --- /dev/null +++ b/src/decode_nesting.h @@ -0,0 +1,419 @@ +/* ========================================================================== + * decode_nesting.c -- All inline implementation of QCBORDecodeNesting + * + * Copyright (c) 2016-2018, The Linux Foundation. + * Copyright (c) 2018-2024, Laurence Lundblade. + * Copyright (c) 2021, Arm Limited. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Forked from qcbor_decode.c on 11/28/24 + * ========================================================================== */ + +#ifndef decode_nesting_h +#define decode_nesting_h + +#include "qcbor/qcbor_private.h" + + +/* When this was not all explicitly inline, the compiler decided to + * inline everything on its own, so we know there's no loss by + * making it all inline. + */ + +static inline void +DecodeNesting_Init(QCBORDecodeNesting *pNesting) +{ + /* Assumes that *pNesting has been zero'd before this call. */ + pNesting->pLevels[0].uLevelType = QCBOR_TYPE_BYTE_STRING; + pNesting->pCurrent = &(pNesting->pLevels[0]); +} + + +static inline bool +DecodeNesting_IsCurrentDefiniteLength(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { + /* Not a map or array */ + return false; + } + +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS + if(pNesting->pCurrent->u.ma.uCountTotal == QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH) { + /* Is indefinite */ + return false; + } + +#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ + + /* All checks passed; is a definte length map or array */ + return true; +} + + +static inline bool +DecodeNesting_IsBoundedType(const QCBORDecodeNesting *pNesting, uint8_t uType) +{ + if(pNesting->pCurrentBounded == NULL) { + return false; + } + + uint8_t uItemDataType = pNesting->pCurrentBounded->uLevelType; +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) { + uItemDataType = QCBOR_TYPE_ARRAY; + } +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + + if(uItemDataType != uType) { + return false; + } + + return true; +} + + +static inline bool +DecodeNesting_IsCurrentBounded(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { + return true; + } + if(pNesting->pCurrent->u.ma.uStartOffset != QCBOR_NON_BOUNDED_OFFSET) { + return true; + } + return false; +} + + +static inline bool +DecodeNesting_IsBoundedEmpty(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrentBounded->u.ma.uCountCursor == QCBOR_COUNT_INDICATES_ZERO_LENGTH) { + return true; + } else { + return false; + } +} + + +static inline bool +DecodeNesting_IsAtEndOfBoundedLevel(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrentBounded == NULL) { + /* No bounded map or array set up */ + return false; + } + if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { + /* Not a map or array; end of those is by byte count */ + return false; + } + if(!DecodeNesting_IsCurrentBounded(pNesting)) { + /* In a traveral at a level deeper than the bounded level */ + return false; + } + /* Works for both definite- and indefinitelength maps/arrays */ + if(pNesting->pCurrentBounded->u.ma.uCountCursor != 0 && + pNesting->pCurrentBounded->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) { + /* Count is not zero, still unconsumed item */ + return false; + } + /* All checks passed, got to the end of an array or map*/ + return true; +} + + +static inline bool +DecodeNesting_IsEndOfDefiniteLengthMapOrArray(const QCBORDecodeNesting *pNesting) +{ + /* Must only be called on map / array */ + if(pNesting->pCurrent->u.ma.uCountCursor == 0) { + return true; + } else { + return false; + } +} + + +static inline bool +DecodeNesting_IsCurrentTypeMap(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrent->uLevelType == CBOR_MAJOR_TYPE_MAP) { + return true; + } else { + return false; + } +} + + +static inline bool +DecodeNesting_IsCurrentAtTop(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrent == &(pNesting->pLevels[0])) { + return true; + } else { + return false; + } +} + + +static inline bool +DecodeNesting_IsCurrentBstrWrapped(const QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { + /* is a byte string */ + return true; + } + return false; +} + + +static inline uint8_t +DecodeNesting_GetCurrentLevel(const QCBORDecodeNesting *pNesting) +{ + const ptrdiff_t nLevel = pNesting->pCurrent - &(pNesting->pLevels[0]); + /* Limit in DecodeNesting_Descend against more than + * QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe + */ + return (uint8_t)nLevel; +} + + + + +static inline void +DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(QCBORDecodeNesting *pNesting) +{ + /* Only call on a definite-length array / map */ + pNesting->pCurrent->u.ma.uCountCursor--; +} + + +static inline void +DecodeNesting_ZeroMapOrArrayCount(QCBORDecodeNesting *pNesting) +{ + pNesting->pCurrent->u.ma.uCountCursor = 0; +} + +static inline void +DecodeNesting_ResetMapOrArrayCount(QCBORDecodeNesting *pNesting) +{ + if(pNesting->pCurrent->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) { + pNesting->pCurrentBounded->u.ma.uCountCursor = pNesting->pCurrentBounded->u.ma.uCountTotal; + } +} + +static inline void +DecodeNesting_ReverseDecrement(QCBORDecodeNesting *pNesting) +{ + /* Only call on a definite-length array / map */ + pNesting->pCurrent->u.ma.uCountCursor++; +} + + + + +static inline void +DecodeNesting_ClearBoundedMode(QCBORDecodeNesting *pNesting) +{ + pNesting->pCurrent->u.ma.uStartOffset = QCBOR_NON_BOUNDED_OFFSET; +} + + +static inline QCBORError +DecodeNesting_Descend(QCBORDecodeNesting *pNesting, uint8_t uType) +{ + /* Error out if nesting is too deep */ + if(pNesting->pCurrent >= &(pNesting->pLevels[QCBOR_MAX_ARRAY_NESTING])) { + return QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP; + } + + /* The actual descend */ + pNesting->pCurrent++; + + pNesting->pCurrent->uLevelType = uType; + + return QCBOR_SUCCESS; +} + +static inline QCBORError +DecodeNesting_DescendMapOrArray(QCBORDecodeNesting *pNesting, + const uint8_t uQCBORType, + const uint16_t uCount) +{ + QCBORError uError = QCBOR_SUCCESS; + + if(uCount == 0) { + /* Nothing to do for empty definite-length 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 */ + } + + /* Rely on check in QCBOR_Private_DecodeArrayOrMap() for definite-length + * arrays and maps that are too long */ + + uError = DecodeNesting_Descend(pNesting, uQCBORType); + if(uError != QCBOR_SUCCESS) { + goto Done; + } + + pNesting->pCurrent->u.ma.uCountCursor = uCount; + pNesting->pCurrent->u.ma.uCountTotal = uCount; + + DecodeNesting_ClearBoundedMode(pNesting); + +Done: + return uError;; +} + + +static inline QCBORError +DecodeNesting_DescendIntoBstrWrapped(QCBORDecodeNesting *pNesting, + uint32_t uEndOffset, + uint32_t uStartOffset) +{ + QCBORError uError; + + uError = DecodeNesting_Descend(pNesting, QCBOR_TYPE_BYTE_STRING); + if(uError != QCBOR_SUCCESS) { + goto Done; + } + + /* Fill in the new byte string level */ + pNesting->pCurrent->u.bs.uSavedEndOffset = uEndOffset; + pNesting->pCurrent->u.bs.uBstrStartOffset = uStartOffset; + + /* Bstr wrapped levels are always bounded */ + pNesting->pCurrentBounded = pNesting->pCurrent; + +Done: + return uError;; +} + + +static inline void +DecodeNesting_Ascend(QCBORDecodeNesting *pNesting) +{ + pNesting->pCurrent--; +} + + + + +static inline void +DecodeNesting_SetCurrentToBoundedLevel(QCBORDecodeNesting *pNesting) +{ + pNesting->pCurrent = pNesting->pCurrentBounded; +} + +static inline void +DecodeNesting_SetMapOrArrayBoundedMode(QCBORDecodeNesting *pNesting, bool bIsEmpty, size_t uStart) +{ + /* Should be only called on maps and arrays */ + /* + * DecodeNesting_EnterBoundedMode() checks to be sure uStart is not + * larger than DecodeNesting_EnterBoundedMode which keeps it less than + * uin32_t so the cast is safe. + */ + pNesting->pCurrent->u.ma.uStartOffset = (uint32_t)uStart; + + if(bIsEmpty) { + pNesting->pCurrent->u.ma.uCountCursor = QCBOR_COUNT_INDICATES_ZERO_LENGTH; + } +} + +static inline QCBORError +DecodeNesting_EnterBoundedMapOrArray(QCBORDecodeNesting *pNesting, + bool bIsEmpty, + size_t uOffset) +{ + /* + * Should only be called on map/array. + * + * Have descended into this before this is called. The job here is + * just to mark it in bounded mode. + * + * Check against QCBOR_MAX_DECODE_INPUT_SIZE make sure that + * uOffset doesn't collide with QCBOR_NON_BOUNDED_OFFSET. + * + * Cast of uOffset to uint32_t for cases where SIZE_MAX < UINT32_MAX. + */ + if((uint32_t)uOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) { + return QCBOR_ERR_INPUT_TOO_LARGE; + } + + pNesting->pCurrentBounded = pNesting->pCurrent; + + DecodeNesting_SetMapOrArrayBoundedMode(pNesting, bIsEmpty, uOffset); + + return QCBOR_SUCCESS; +} + + +static inline uint32_t +DecodeNesting_GetPreviousBoundedEnd(const QCBORDecodeNesting *pMe) +{ + return pMe->pCurrentBounded->u.bs.uSavedEndOffset; +} + + +static inline uint8_t +DecodeNesting_GetBoundedModeLevel(const QCBORDecodeNesting *pNesting) +{ + const ptrdiff_t nLevel = pNesting->pCurrentBounded - &(pNesting->pLevels[0]); + /* Limit in DecodeNesting_Descend against more than + * QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe + */ + return (uint8_t)nLevel; +} + + + + +static inline uint32_t +DecodeNesting_GetMapOrArrayStart(const QCBORDecodeNesting *pNesting) +{ + return pNesting->pCurrentBounded->u.ma.uStartOffset; +} + + +static inline void +DecodeNesting_LevelUpCurrent(QCBORDecodeNesting *pNesting) +{ + pNesting->pCurrent = pNesting->pCurrentBounded - 1; +} + + +static inline void +DecodeNesting_LevelUpBounded(QCBORDecodeNesting *pNesting) +{ + while(pNesting->pCurrentBounded != &(pNesting->pLevels[0])) { + pNesting->pCurrentBounded--; + if(DecodeNesting_IsCurrentBounded(pNesting)) { + break; + } + } +} + + + + +static inline void +DecodeNesting_PrepareForMapSearch(QCBORDecodeNesting *pNesting, + QCBORDecodeNesting *pSave) +{ + *pSave = *pNesting; +} + + +static inline void +DecodeNesting_RestoreFromMapSearch(QCBORDecodeNesting *pNesting, + const QCBORDecodeNesting *pSave) +{ + *pNesting = *pSave; +} + +#endif /* decode_nesting_h */ diff --git a/src/decode_private.h b/src/decode_private.h index 506c668..c1d6f9f 100644 --- a/src/decode_private.h +++ b/src/decode_private.h @@ -1,79 +1,113 @@ -/*============================================================================== - Copyright (c) 2016-2018, The Linux Foundation. - Copyright (c) 2018-2024, Laurence Lundblade. - Copyright (c) 2021, Arm Limited. - All rights reserved. - - Created on 11/14/24 from qcbor_decode.c - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of The Linux Foundation nor the names of its - contributors, nor the name "Laurence Lundblade" may be used to - endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - =============================================================================*/ +/* ========================================================================== + * decode_private.c -- semi-private & inline functions for qcbor_decode.c + * + * Copyright (c) 2016-2018, The Linux Foundation. + * Copyright (c) 2018-2024, Laurence Lundblade. + * Copyright (c) 2021, Arm Limited. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Forked from qcbor_decode.c on 11/14/24 + * ========================================================================== */ + #ifndef decode_private_h #define decode_private_h +#include "qcbor/qcbor_decode.h" +#include "qcbor/qcbor_spiffy_decode.h" /* For QCBORItemCallback */ + +/* These are decode functions used by the spiffy decode and number decode + * implementation. They are internal linkage and nothing to do with + * the public decode interface. + */ +/* Semi-private function. See qcbor_decode.c */ QCBORError QCBORDecode_Private_GetNextTagContent(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem); -void -QCBORDecode_Private_ProcessTagItemMulti(QCBORDecodeContext *pMe, - QCBORItem *pItem, - const uint8_t uTagRequirement, - const uint8_t uQCBORTypes[], - const uint64_t uTagNumbers[], - QCBORTagContentCallBack *pfCB, - size_t uOffset); - - -void -QCBORDecode_Private_ProcessTagItem(QCBORDecodeContext *pMe, - QCBORItem *pItem, - const uint8_t uTagRequirement, - const uint8_t uQCBORTypes[], - const uint64_t uTagNumber, - QCBORTagContentCallBack *pfCB, - size_t uOffset); +/* Semi-private function. See qcbor_decode.c */ void QCBORDecode_Private_GetItemInMapNoCheckSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uQcborType, - QCBORItem *pItem, - size_t *puOffset); + const char *szLabel, + const uint8_t uQcborType, + QCBORItem *pItem, + size_t *puOffset); +/* Semi-private function. See qcbor_decode.c */ void QCBORDecode_Private_GetItemInMapNoCheckN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uQcborType, - QCBORItem *pItem, - size_t *puOffset); + const int64_t nLabel, + const uint8_t uQcborType, + QCBORItem *pItem, + size_t *puOffset); + + +/* Semi-private function. See qcbor_decode.c */ +uint64_t +QCBORDecode_Private_UnMapTagNumber(const QCBORDecodeContext *pMe, + const uint16_t uMappedTagNumber); + +/* Semi-private function. See qcbor_decode.c */ +QCBORError +QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe, + const QCBORItem *pItemToConsume, + bool *pbBreak, + uint8_t *puNextNestLevel); + +/* Semi-private function. See qcbor_decode.c */ +QCBORError +QCBORDecode_Private_GetItemChecks(QCBORDecodeContext *pMe, + QCBORError uErr, + const size_t uOffset, + QCBORItem *pDecodedItem); + +/* Semi-private function. See qcbor_decode.c */ +QCBORError +QCBORDecode_Private_NestLevelAscender(QCBORDecodeContext *pMe, + bool bMarkEnd, + bool *pbBreak); + + +typedef struct { + void *pCBContext; + QCBORItemCallback pfCallback; +} MapSearchCallBack; + +typedef struct { + size_t uStartOffset; + uint16_t uItemCount; +} MapSearchInfo; + +/* Semi-private function. See qcbor_decode.c */ +QCBORError +QCBORDecode_Private_MapSearch(QCBORDecodeContext *pMe, + QCBORItem *pItemArray, + MapSearchInfo *pInfo, + MapSearchCallBack *pCallBack); + + +/* Semi-private function. See qcbor_decode.c */ +QCBORError +QCBORDecode_Private_ExitBoundedLevel(QCBORDecodeContext *pMe, + const uint32_t uEndOffset); + + +static inline void +QCBORDecode_Private_SaveTagNumbers(QCBORDecodeContext *pMe, const QCBORItem *pItem) +{ +#ifndef QCBOR_DISABLE_TAGS + memcpy(pMe->auLastTags, pItem->auTagNumbers, sizeof(pItem->auTagNumbers)); +#else /* ! QCBOR_DISABLE_TAGS */ + (void)pMe; + (void)pItem; +#endif /* ! QCBOR_DISABLE_TAGS */ +} + + static inline void QCBORDecode_Private_GetAndTell(QCBORDecodeContext *pMe, QCBORItem *Item, size_t *uOffset) @@ -84,7 +118,7 @@ QCBORDecode_Private_GetAndTell(QCBORDecodeContext *pMe, QCBORItem *Item, size_t } *uOffset = QCBORDecode_Tell(pMe); -#else +#else /* ! QCBOR_DISABLE_TAGS */ *uOffset = SIZE_MAX; #endif /* ! QCBOR_DISABLE_TAGS */ @@ -92,4 +126,25 @@ QCBORDecode_Private_GetAndTell(QCBORDecodeContext *pMe, QCBORItem *Item, size_t } + + +/* Semi-private function. See qcbor_tag_decode.c */ +void +QCBORDecode_Private_ProcessTagItemMulti(QCBORDecodeContext *pMe, + QCBORItem *pItem, + const uint8_t uTagRequirement, + const uint8_t uQCBORTypes[], + const uint64_t uTagNumbers[], + QCBORTagContentCallBack *pfCB, + size_t uOffset); + +/* Semi-private function. See qcbor_tag_decode.c */ +void +QCBORDecode_Private_ProcessTagItem(QCBORDecodeContext *pMe, + QCBORItem *pItem, + const uint8_t uTagRequirement, + const uint8_t uQCBORTypes[], + const uint64_t uTagNumber, + QCBORTagContentCallBack *pfCB, + size_t uOffset); #endif /* decode_private_h */ diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c index 7e2abad..257f8e7 100644 --- a/src/qcbor_decode.c +++ b/src/qcbor_decode.c @@ -36,6 +36,9 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "qcbor/qcbor_spiffy_decode.h" #include "qcbor/qcbor_tag_decode.h" #include "ieee754.h" /* Does not use math.h */ +#include "decode_private.h" +#include "decode_nesting.h" + #if (defined(__GNUC__) && !defined(__clang__)) @@ -127,2664 +130,460 @@ QCBORItem_IsIndefiniteLengthMapOrArray(const QCBORItem Item) #endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ } -/* 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 bool -QCBORItem_MatchLabel(const QCBORItem Item1, const QCBORItem Item2) -{ - if(Item1.uLabelType == QCBOR_TYPE_INT64) { - if(Item2.uLabelType == QCBOR_TYPE_INT64 && Item1.label.int64 == Item2.label.int64) { - return true; - } -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - } 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; - } - } else if(Item1.uLabelType == QCBOR_TYPE_BYTE_STRING) { - if(Item2.uLabelType == QCBOR_TYPE_BYTE_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) { - return true; - } - } else if(Item1.uLabelType == QCBOR_TYPE_UINT64) { - if(Item2.uLabelType == QCBOR_TYPE_UINT64 && Item1.label.uint64 == Item2.label.uint64) { - return true; - } -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - } - - /* Other label types are never matched */ - return false; -} - -/* - Returns true if Item1 and Item2 are the same type - or if either are of QCBOR_TYPE_ANY. - */ -static bool -QCBORItem_MatchType(const QCBORItem Item1, const QCBORItem Item2) -{ - if(Item1.uDataType == Item2.uDataType) { - return true; - } else if(Item1.uDataType == QCBOR_TYPE_ANY) { - return true; - } else if(Item2.uDataType == QCBOR_TYPE_ANY) { - return true; - } - return false; -} +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS /*=========================================================================== - DecodeNesting -- Tracking array/map/sequence/bstr-wrapped nesting - ===========================================================================*/ - -/* - * See comments about and typedef of QCBORDecodeNesting in qcbor_private.h, - * the data structure all these functions work on. - */ - + QCBORStringAllocate -- STRING ALLOCATOR INVOCATION -static uint8_t -DecodeNesting_GetCurrentLevel(const QCBORDecodeNesting *pNesting) -{ - const ptrdiff_t nLevel = pNesting->pCurrent - &(pNesting->pLevels[0]); - /* Limit in DecodeNesting_Descend against more than - * QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe - */ - return (uint8_t)nLevel; -} + The following four functions are pretty wrappers for invocation of + the string allocator supplied by the caller. + ===========================================================================*/ -static uint8_t -DecodeNesting_GetBoundedModeLevel(const QCBORDecodeNesting *pNesting) +static void +StringAllocator_Free(const QCBORInternalAllocator *pMe, const void *pMem) { - const ptrdiff_t nLevel = pNesting->pCurrentBounded - &(pNesting->pLevels[0]); - /* Limit in DecodeNesting_Descend against more than - * QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe + /* This cast to uintptr_t suppresses the "-Wcast-qual" warnings. + * This is the one place where the const needs to be cast away so const can + * be use in the rest of the code. */ - return (uint8_t)nLevel; -} - - -static uint32_t -DecodeNesting_GetMapOrArrayStart(const QCBORDecodeNesting *pNesting) -{ - return pNesting->pCurrentBounded->u.ma.uStartOffset; + (pMe->pfAllocator)(pMe->pAllocateCxt, (void *)(uintptr_t)pMem, 0); } - -static bool -DecodeNesting_IsBoundedEmpty(const QCBORDecodeNesting *pNesting) +// StringAllocator_Reallocate called with pMem NULL is +// equal to StringAllocator_Allocate() +static UsefulBuf +StringAllocator_Reallocate(const QCBORInternalAllocator *pMe, + const void *pMem, + size_t uSize) { - if(pNesting->pCurrentBounded->u.ma.uCountCursor == QCBOR_COUNT_INDICATES_ZERO_LENGTH) { - return true; - } else { - return false; - } + /* See comment in StringAllocator_Free() */ + return (pMe->pfAllocator)(pMe->pAllocateCxt, (void *)(uintptr_t)pMem, uSize); } - -static bool -DecodeNesting_IsCurrentAtTop(const QCBORDecodeNesting *pNesting) +static UsefulBuf +StringAllocator_Allocate(const QCBORInternalAllocator *pMe, size_t uSize) { - if(pNesting->pCurrent == &(pNesting->pLevels[0])) { - return true; - } else { - return false; - } + return (pMe->pfAllocator)(pMe->pAllocateCxt, NULL, uSize); } - -static bool -DecodeNesting_IsCurrentDefiniteLength(const QCBORDecodeNesting *pNesting) +static void +StringAllocator_Destruct(const QCBORInternalAllocator *pMe) { - if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { - /* Not a map or array */ - return false; - } - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS - if(pNesting->pCurrent->u.ma.uCountTotal == QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH) { - /* Is indefinite */ - return false; + /* See comment in StringAllocator_Free() */ + if(pMe->pfAllocator) { + (pMe->pfAllocator)(pMe->pAllocateCxt, NULL, 0); } - -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - - /* All checks passed; is a definte length map or array */ - return true; } +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ -static bool -DecodeNesting_IsCurrentBstrWrapped(const QCBORDecodeNesting *pNesting) -{ - if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { - /* is a byte string */ - return true; - } - return false; -} -static bool -DecodeNesting_IsCurrentBounded(const QCBORDecodeNesting *pNesting) -{ - if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { - return true; - } - if(pNesting->pCurrent->u.ma.uStartOffset != QCBOR_NON_BOUNDED_OFFSET) { - return true; - } - return false; -} +/*=========================================================================== + QCBORDecode -- The main implementation of CBOR decoding -static void -DecodeNesting_SetMapOrArrayBoundedMode(QCBORDecodeNesting *pNesting, bool bIsEmpty, size_t uStart) + See qcbor/qcbor_decode.h for definition of the object + used here: QCBORDecodeContext + ===========================================================================*/ +/* + * Public function, see header file + */ +void +QCBORDecode_Init(QCBORDecodeContext *pMe, + UsefulBufC EncodedCBOR, + QCBORDecodeMode uConfigFlags) { - /* Should be only called on maps and arrays */ - /* - * DecodeNesting_EnterBoundedMode() checks to be sure uStart is not - * larger than DecodeNesting_EnterBoundedMode which keeps it less than - * uin32_t so the cast is safe. + memset(pMe, 0, sizeof(QCBORDecodeContext)); + UsefulInputBuf_Init(&(pMe->InBuf), EncodedCBOR); + /* Don't bother with error check on decode mode. If a bad value is + * passed it will just act as if the default normal mode of 0 was set. */ - pNesting->pCurrent->u.ma.uStartOffset = (uint32_t)uStart; - - if(bIsEmpty) { - pNesting->pCurrent->u.ma.uCountCursor = QCBOR_COUNT_INDICATES_ZERO_LENGTH; - } -} - - -static void -DecodeNesting_ClearBoundedMode(QCBORDecodeNesting *pNesting) -{ - pNesting->pCurrent->u.ma.uStartOffset = QCBOR_NON_BOUNDED_OFFSET; -} - - -static bool -DecodeNesting_IsAtEndOfBoundedLevel(const QCBORDecodeNesting *pNesting) -{ - if(pNesting->pCurrentBounded == NULL) { - /* No bounded map or array set up */ - return false; - } - if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) { - /* Not a map or array; end of those is by byte count */ - return false; - } - if(!DecodeNesting_IsCurrentBounded(pNesting)) { - /* In a traveral at a level deeper than the bounded level */ - return false; - } - /* Works for both definite- and indefinitelength maps/arrays */ - if(pNesting->pCurrentBounded->u.ma.uCountCursor != 0 && - pNesting->pCurrentBounded->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) { - /* Count is not zero, still unconsumed item */ - return false; - } - /* All checks passed, got to the end of an array or map*/ - return true; -} + pMe->uDecodeMode = (uint32_t)uConfigFlags; + DecodeNesting_Init(&(pMe->nesting)); + /* Inialize me->auMappedTags to CBOR_TAG_INVALID16. See + * GetNext_TaggedItem() and MapTagNumber(). */ + memset(pMe->auMappedTags, 0xff, sizeof(pMe->auMappedTags)); -static bool -DecodeNesting_IsEndOfDefiniteLengthMapOrArray(const QCBORDecodeNesting *pNesting) -{ - /* Must only be called on map / array */ - if(pNesting->pCurrent->u.ma.uCountCursor == 0) { - return true; - } else { - return false; - } + pMe->uTagNumberCheckOffset = SIZE_MAX; } -static bool -DecodeNesting_IsCurrentTypeMap(const QCBORDecodeNesting *pNesting) +/* + * Public function, see header file + */ +void +QCBORDecode_CompatibilityV1(QCBORDecodeContext *pMe) { - if(pNesting->pCurrent->uLevelType == CBOR_MAJOR_TYPE_MAP) { - return true; - } else { - return false; - } + pMe->uDecodeMode |= QCBOR_DECODE_ALLOW_UNPROCESSED_TAG_NUMBERS; +#ifndef QCBOR_DISABLE_TAGS + QCBORDecode_InstallTagDecoders(pMe, QCBORDecode_TagDecoderTablev1, NULL); +#endif /* ! QCBOR_DISABLE_TAGS */ } -static bool -DecodeNesting_IsBoundedType(const QCBORDecodeNesting *pNesting, uint8_t uType) -{ - if(pNesting->pCurrentBounded == NULL) { - return false; - } - - uint8_t uItemDataType = pNesting->pCurrentBounded->uLevelType; -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) { - uItemDataType = QCBOR_TYPE_ARRAY; - } -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - - if(uItemDataType != uType) { - return false; - } - return true; -} +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS -static void -DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(QCBORDecodeNesting *pNesting) +/* + * Public function, see header file + */ +void +QCBORDecode_SetUpAllocator(QCBORDecodeContext *pMe, + QCBORStringAllocate pfAllocateFunction, + void *pAllocateContext, + bool bAllStrings) { - /* Only call on a definite-length array / map */ - pNesting->pCurrent->u.ma.uCountCursor--; + pMe->StringAllocator.pfAllocator = pfAllocateFunction; + pMe->StringAllocator.pAllocateCxt = pAllocateContext; + pMe->bStringAllocateAll = bAllStrings; } +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ -static void -DecodeNesting_ReverseDecrement(QCBORDecodeNesting *pNesting) -{ - /* Only call on a definite-length array / map */ - pNesting->pCurrent->u.ma.uCountCursor++; -} - -static void -DecodeNesting_Ascend(QCBORDecodeNesting *pNesting) -{ - pNesting->pCurrent--; -} -static QCBORError -DecodeNesting_Descend(QCBORDecodeNesting *pNesting, uint8_t uType) -{ - /* Error out if nesting is too deep */ - if(pNesting->pCurrent >= &(pNesting->pLevels[QCBOR_MAX_ARRAY_NESTING])) { - return QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP; - } - /* The actual descend */ - pNesting->pCurrent++; +/* + * Decoding items is done in six layers, one calling the next one + * down. If a layer has no work to do for a particular item, it + * returns quickly. + * + * 1. QCBORDecode_Private_GetNextTagContent - The top layer processes + * tagged data items, turning them into the local C representation. + * For the most simple it is just associating a QCBOR_TYPE with the + * data. For the complex ones that an aggregate of data items, there + * is some further decoding and some limited recursion. + * + * 2. QCBORDecode_Private_GetNextMapOrArray - This manages the + * beginnings and ends of maps and arrays. It tracks descending into + * and ascending out of maps/arrays. It processes breaks that + * terminate indefinite-length maps and arrays. + * + * 3. QCBORDecode_Private_GetNextMapEntry - This handles the combining + * of two items, the label and the data, that make up a map entry. It + * only does work on maps. It combines the label and data items into + * one labeled item. + * + * 4. QCBORDecode_Private_GetNextTagNumber - This decodes type 6 tag + * numbers. It turns the tag numbers into bit flags associated with + * the data item. No actual decoding of the contents of the tag is + * performed here. + * + * 5. QCBORDecode_Private_GetNextFullString - This assembles the + * sub-items that make up an indefinite-length string into one string + * item. It uses the string allocator to create contiguous space for + * the item. It processes all breaks that are part of + * indefinite-length strings. + * + * 6. QCBOR_Private_DecodeAtomicDataItem - This decodes the atomic + * data items in CBOR. Each atomic data item has a "major type", an + * integer "argument" and optionally some content. For text and byte + * strings, the content is the bytes that make up the string. These + * are the smallest data items that are considered to be well-formed. + * The content may also be other data items in the case of aggregate + * types. They are not handled in this layer. + * + * This uses about 350 bytes of stack. This number comes from + * instrumenting (printf address of stack variables) the code on x86 + * compiled for size optimization. + */ - pNesting->pCurrent->uLevelType = uType; - return QCBOR_SUCCESS; -} +/* + * Note about use of int and unsigned variables. + * + * See http://www.unix.org/whitepapers/64bit.html for reasons int is + * used carefully here, and in particular why it isn't used in the + * public interface. Also see + * https://stackoverflow.com/questions/17489857/why-is-int-typically-32-bit-on-64-bit-compilers + * + * Int is used for values that need less than 16-bits and would be + * subject to integer promotion and result in complaining from static + * analyzers. + */ -static QCBORError -DecodeNesting_EnterBoundedMapOrArray(QCBORDecodeNesting *pNesting, - bool bIsEmpty, - size_t uOffset) -{ - /* - * Should only be called on map/array. - * - * Have descended into this before this is called. The job here is - * just to mark it in bounded mode. - * - * Check against QCBOR_MAX_DECODE_INPUT_SIZE make sure that - * uOffset doesn't collide with QCBOR_NON_BOUNDED_OFFSET. - * - * Cast of uOffset to uint32_t for cases where SIZE_MAX < UINT32_MAX. - */ - if((uint32_t)uOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) { - return QCBOR_ERR_INPUT_TOO_LARGE; - } +/** + * @brief Decode the CBOR head, the type and argument. + * + * @param[in] pUInBuf The input buffer to read from. + * @param[in] uConfigFlags Decode mode flags. + * @param[out] pnMajorType The decoded major type. + * @param[out] puArgument The decoded argument. + * @param[out] pnAdditionalInfo The decoded Lower 5 bits of initial byte. + * + * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved features + * @retval QCBOR_ERR_HIT_END Unexpected end of input + * + * This decodes the CBOR "head" that every CBOR data item has. See + * longer description in QCBOREncode_EncodeHead(). + * + * This does the network to host byte order conversion. The conversion + * here also provides the conversion for floats in addition to that + * for lengths, tags and integer values. + * + * The int type is preferred to uint8_t for some variables as this + * avoids integer promotions, can reduce code size and makes static + * analyzers happier. + */ +static QCBORError +QCBOR_Private_DecodeHead(UsefulInputBuf *pUInBuf, +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + QCBORDecodeMode uConfigFlags, +#endif + int *pnMajorType, + uint64_t *puArgument, + int *pnAdditionalInfo) +{ + QCBORError uReturn; + uint64_t uArgument; - pNesting->pCurrentBounded = pNesting->pCurrent; + /* Get and break down initial byte that every CBOR data item has */ + const int nInitialByte = (int)UsefulInputBuf_GetByte(pUInBuf); + const int nTmpMajorType = nInitialByte >> 5; + const int nAdditionalInfo = nInitialByte & 0x1f; - DecodeNesting_SetMapOrArrayBoundedMode(pNesting, bIsEmpty, uOffset); + if(nAdditionalInfo >= LEN_IS_ONE_BYTE && nAdditionalInfo <= LEN_IS_EIGHT_BYTES) { + /* Need to get 1,2,4 or 8 additional argument bytes. Map + * LEN_IS_ONE_BYTE..LEN_IS_EIGHT_BYTES to actual length. + */ + static const uint8_t aIterate[] = {1,2,4,8}; - return QCBOR_SUCCESS; -} + /* Loop getting all the bytes in the argument */ + uArgument = 0; + for(int i = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE]; i; i--) { + /* This shift-and-add gives the endian conversion. */ + uArgument = (uArgument << 8) + UsefulInputBuf_GetByte(pUInBuf); + } +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + /* If requested, check that argument is in preferred form */ + if(uConfigFlags & QCBOR_DECODE_ONLY_PREFERRED_NUMBERS) { + uint64_t uMinArgument; -static QCBORError -DecodeNesting_DescendMapOrArray(QCBORDecodeNesting *pNesting, - const uint8_t uQCBORType, - const uint16_t uCount) -{ - QCBORError uError = QCBOR_SUCCESS; + if(nAdditionalInfo == LEN_IS_ONE_BYTE) { + if(uArgument < 24) { + uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE; + goto Done; + } + } else { + if(nTmpMajorType != CBOR_MAJOR_TYPE_SIMPLE) { + /* Check only if not a floating-point number */ + int nArgLen = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE - 1]; + uMinArgument = UINT64_MAX >> ((int)sizeof(uint64_t) - nArgLen) * 8; + if(uArgument <= uMinArgument) { + uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE; + goto Done; + } + } + } + } +#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - if(uCount == 0) { - /* Nothing to do for empty definite-length arrays. They are just are - * effectively the same as an item that is not a map or array. - */ + } else if(nAdditionalInfo >= ADDINFO_RESERVED1 && nAdditionalInfo <= ADDINFO_RESERVED3) { + /* The reserved and thus-far unused additional info values */ + uReturn = QCBOR_ERR_UNSUPPORTED; goto Done; - /* Empty indefinite-length maps and arrays are handled elsewhere */ - } + } else { +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + if(uConfigFlags & QCBOR_DECODE_NO_INDEF_LENGTH && nAdditionalInfo == LEN_IS_INDEFINITE) { + uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE; + goto Done; + } +#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - /* Rely on check in QCBOR_Private_DecodeArrayOrMap() for definite-length - * arrays and maps that are too long */ + /* Less than 24, additional info is argument or 31, an + * indefinite-length. No more bytes to get. + */ + uArgument = (uint64_t)nAdditionalInfo; + } - uError = DecodeNesting_Descend(pNesting, uQCBORType); - if(uError != QCBOR_SUCCESS) { + if(UsefulInputBuf_GetError(pUInBuf)) { + uReturn = QCBOR_ERR_HIT_END; goto Done; } - pNesting->pCurrent->u.ma.uCountCursor = uCount; - pNesting->pCurrent->u.ma.uCountTotal = uCount; - - DecodeNesting_ClearBoundedMode(pNesting); + /* All successful if arrived here. */ + uReturn = QCBOR_SUCCESS; + *pnMajorType = nTmpMajorType; + *puArgument = uArgument; + *pnAdditionalInfo = nAdditionalInfo; Done: - return uError;; + return uReturn; } -static void -DecodeNesting_LevelUpCurrent(QCBORDecodeNesting *pNesting) +/** + * @brief Decode integer types, major types 0 and 1. + * + * @param[in] nMajorType The CBOR major type (0 or 1). + * @param[in] uArgument The argument from the head. + * @param[in] nAdditionalInfo So it can be error-checked. + * @param[out] pDecodedItem The filled in decoded item. + * + * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered. + * @retval QCBOR_ERR_BAD_INT nAdditionalInfo indicated indefinte. + * + * Must only be called when major type is 0 or 1. + * + * CBOR doesn't explicitly specify two's compliment for integers but + * all CPUs use it these days and the test vectors in the RFC are + * so. All integers in encoded CBOR are unsigned and the CBOR major + * type indicates positive or negative. CBOR can express positive + * integers up to 2^64 - 1 negative integers down to -2^64. Note that + * negative numbers can be one more + * away from zero than positive because there is no negative zero. + * + * The "65-bit negs" are values CBOR can encode that can't fit + * into an int64_t or uint64_t. They decoded as a special type + * QCBOR_TYPE_65BIT_NEG_INT. Not that this type does NOT + * take into account the offset of one for CBOR negative integers. + * It must be applied to get the correct value. Applying this offset + * would overflow a uint64_t. + */ +static QCBORError +QCBOR_Private_DecodeInteger(const int nMajorType, + const uint64_t uArgument, + const int nAdditionalInfo, + QCBORItem *pDecodedItem) { - pNesting->pCurrent = pNesting->pCurrentBounded - 1; -} - + QCBORError uReturn = QCBOR_SUCCESS; -static void -DecodeNesting_LevelUpBounded(QCBORDecodeNesting *pNesting) -{ - while(pNesting->pCurrentBounded != &(pNesting->pLevels[0])) { - pNesting->pCurrentBounded--; - if(DecodeNesting_IsCurrentBounded(pNesting)) { - break; - } + if(nAdditionalInfo == LEN_IS_INDEFINITE) { + uReturn = QCBOR_ERR_BAD_INT; + goto Done; } -} - -static void -DecodeNesting_SetCurrentToBoundedLevel(QCBORDecodeNesting *pNesting) -{ - pNesting->pCurrent = pNesting->pCurrentBounded; -} + if(nMajorType == CBOR_MAJOR_TYPE_POSITIVE_INT) { + if (uArgument <= INT64_MAX) { + pDecodedItem->val.int64 = (int64_t)uArgument; + pDecodedItem->uDataType = QCBOR_TYPE_INT64; + } else { + pDecodedItem->val.uint64 = uArgument; + pDecodedItem->uDataType = QCBOR_TYPE_UINT64; + } -static QCBORError -DecodeNesting_DescendIntoBstrWrapped(QCBORDecodeNesting *pNesting, - uint32_t uEndOffset, - uint32_t uStartOffset) -{ - QCBORError uError; + } else { + if(uArgument <= INT64_MAX) { + /* INT64_MIN is one further away from 0 than INT64_MAX + * so the -1 here doesn't overflow. */ + pDecodedItem->val.int64 = (-(int64_t)uArgument) - 1; + pDecodedItem->uDataType = QCBOR_TYPE_INT64; - uError = DecodeNesting_Descend(pNesting, QCBOR_TYPE_BYTE_STRING); - if(uError != QCBOR_SUCCESS) { - goto Done; + } else { + pDecodedItem->val.uint64 = uArgument; + pDecodedItem->uDataType = QCBOR_TYPE_65BIT_NEG_INT; + } } - /* Fill in the new byte string level */ - pNesting->pCurrent->u.bs.uSavedEndOffset = uEndOffset; - pNesting->pCurrent->u.bs.uBstrStartOffset = uStartOffset; - - /* Bstr wrapped levels are always bounded */ - pNesting->pCurrentBounded = pNesting->pCurrent; - Done: - return uError;; + return uReturn; } -static void -DecodeNesting_ZeroMapOrArrayCount(QCBORDecodeNesting *pNesting) +/** + * @brief Decode text and byte strings + * + * @param[in] pMe Decoder context. + * @param[in] bAllocate Whether to allocate and copy string. + * @param[in] nMajorType Whether it is a byte or text string. + * @param[in] uStrLen The length of the string. + * @param[in] nAdditionalInfo Whether it is an indefinite-length string. + * @param[out] pDecodedItem The filled-in decoded item. + * + * @retval QCBOR_ERR_HIT_END Unexpected end of input. + * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. + * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. + * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Allocation requested, but no allocator + * + * This reads @c uStrlen bytes from the input and fills in @c + * pDecodedItem. If @c bAllocate is true, then memory for the string + * is allocated. + */ +static QCBORError +QCBOR_Private_DecodeString(QCBORDecodeContext *pMe, + const bool bAllocate, + const int nMajorType, + const uint64_t uStrLen, + const int nAdditionalInfo, + QCBORItem *pDecodedItem) { - pNesting->pCurrent->u.ma.uCountCursor = 0; -} - + QCBORError uReturn = QCBOR_SUCCESS; -static void -DecodeNesting_ResetMapOrArrayCount(QCBORDecodeNesting *pNesting) -{ - if(pNesting->pCurrent->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) { - pNesting->pCurrentBounded->u.ma.uCountCursor = pNesting->pCurrentBounded->u.ma.uCountTotal; - } -} + /* ---- Figure out the major type ---- */ + #if CBOR_MAJOR_TYPE_BYTE_STRING + 4 != QCBOR_TYPE_BYTE_STRING + #error QCBOR_TYPE_BYTE_STRING not lined up with major type + #endif + #if CBOR_MAJOR_TYPE_TEXT_STRING + 4 != QCBOR_TYPE_TEXT_STRING + #error QCBOR_TYPE_TEXT_STRING not lined up with major type + #endif + pDecodedItem->uDataType = (uint8_t)(nMajorType + 4); -static void -DecodeNesting_Init(QCBORDecodeNesting *pNesting) -{ - /* Assumes that *pNesting has been zero'd before this call. */ - pNesting->pLevels[0].uLevelType = QCBOR_TYPE_BYTE_STRING; - pNesting->pCurrent = &(pNesting->pLevels[0]); -} + if(nAdditionalInfo == LEN_IS_INDEFINITE) { + /* --- Just the head of an indefinite-length string --- */ + pDecodedItem->val.string = (UsefulBufC){NULL, QCBOR_STRING_LENGTH_INDEFINITE}; + } else { + /* --- A definite-length string --- */ + /* --- (which might be a chunk of an indefinte-length string) --- */ -static void -DecodeNesting_PrepareForMapSearch(QCBORDecodeNesting *pNesting, - QCBORDecodeNesting *pSave) -{ - *pSave = *pNesting; -} + /* CBOR lengths can be 64 bits, but size_t is not 64 bits on all + * CPUs. This check makes the casts to size_t below safe. + * + * The max is 4 bytes less than the largest sizeof() so this can be + * tested by putting a SIZE_MAX length in the CBOR test input (no + * one will care the limit on strings is 4 bytes shorter). + */ + if(uStrLen > SIZE_MAX-4) { + uReturn = QCBOR_ERR_STRING_TOO_LONG; + goto Done; + } + const UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(&(pMe->InBuf), (size_t)uStrLen); + if(UsefulBuf_IsNULLC(Bytes)) { + /* Failed to get the bytes for this string item */ + uReturn = QCBOR_ERR_HIT_END; + goto Done; + } -static void -DecodeNesting_RestoreFromMapSearch(QCBORDecodeNesting *pNesting, - const QCBORDecodeNesting *pSave) -{ - *pNesting = *pSave; -} - - -static uint32_t -DecodeNesting_GetPreviousBoundedEnd(const QCBORDecodeNesting *pMe) -{ - return pMe->pCurrentBounded->u.bs.uSavedEndOffset; -} - - - - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS -/*=========================================================================== - QCBORStringAllocate -- STRING ALLOCATOR INVOCATION - - The following four functions are pretty wrappers for invocation of - the string allocator supplied by the caller. - - ===========================================================================*/ - -static void -StringAllocator_Free(const QCBORInternalAllocator *pMe, const void *pMem) -{ - /* This cast to uintptr_t suppresses the "-Wcast-qual" warnings. - * This is the one place where the const needs to be cast away so const can - * be use in the rest of the code. - */ - (pMe->pfAllocator)(pMe->pAllocateCxt, (void *)(uintptr_t)pMem, 0); -} - -// StringAllocator_Reallocate called with pMem NULL is -// equal to StringAllocator_Allocate() -static UsefulBuf -StringAllocator_Reallocate(const QCBORInternalAllocator *pMe, - const void *pMem, - size_t uSize) -{ - /* See comment in StringAllocator_Free() */ - return (pMe->pfAllocator)(pMe->pAllocateCxt, (void *)(uintptr_t)pMem, uSize); -} - -static UsefulBuf -StringAllocator_Allocate(const QCBORInternalAllocator *pMe, size_t uSize) -{ - return (pMe->pfAllocator)(pMe->pAllocateCxt, NULL, uSize); -} - -static void -StringAllocator_Destruct(const QCBORInternalAllocator *pMe) -{ - /* See comment in StringAllocator_Free() */ - if(pMe->pfAllocator) { - (pMe->pfAllocator)(pMe->pAllocateCxt, NULL, 0); - } -} -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ - - - - -/*=========================================================================== - QCBORDecode -- The main implementation of CBOR decoding - - See qcbor/qcbor_decode.h for definition of the object - used here: QCBORDecodeContext - ===========================================================================*/ -/* - * Public function, see header file - */ -void -QCBORDecode_Init(QCBORDecodeContext *pMe, - UsefulBufC EncodedCBOR, - QCBORDecodeMode uConfigFlags) -{ - memset(pMe, 0, sizeof(QCBORDecodeContext)); - UsefulInputBuf_Init(&(pMe->InBuf), EncodedCBOR); - /* Don't bother with error check on decode mode. If a bad value is - * passed it will just act as if the default normal mode of 0 was set. - */ - pMe->uDecodeMode = (uint32_t)uConfigFlags; - DecodeNesting_Init(&(pMe->nesting)); - - /* Inialize me->auMappedTags to CBOR_TAG_INVALID16. See - * GetNext_TaggedItem() and MapTagNumber(). */ - memset(pMe->auMappedTags, 0xff, sizeof(pMe->auMappedTags)); - - pMe->uTagNumberCheckOffset = SIZE_MAX; -} - - -/* - * Public function, see header file - */ -void -QCBORDecode_CompatibilityV1(QCBORDecodeContext *pMe) -{ - pMe->uDecodeMode |= QCBOR_DECODE_ALLOW_UNPROCESSED_TAG_NUMBERS; -#ifndef QCBOR_DISABLE_TAGS - QCBORDecode_InstallTagDecoders(pMe, QCBORDecode_TagDecoderTablev1, NULL); -#endif /* ! QCBOR_DISABLE_TAGS */ -} - - - - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS - -/* - * Public function, see header file - */ -void -QCBORDecode_SetUpAllocator(QCBORDecodeContext *pMe, - QCBORStringAllocate pfAllocateFunction, - void *pAllocateContext, - bool bAllStrings) -{ - pMe->StringAllocator.pfAllocator = pfAllocateFunction; - pMe->StringAllocator.pAllocateCxt = pAllocateContext; - pMe->bStringAllocateAll = bAllStrings; -} -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ - - - - - - -/* - * Decoding items is done in six layers, one calling the next one - * down. If a layer has no work to do for a particular item, it - * returns quickly. - * - * 1. QCBORDecode_Private_GetNextTagContent - The top layer processes - * tagged data items, turning them into the local C representation. - * For the most simple it is just associating a QCBOR_TYPE with the - * data. For the complex ones that an aggregate of data items, there - * is some further decoding and some limited recursion. - * - * 2. QCBORDecode_Private_GetNextMapOrArray - This manages the - * beginnings and ends of maps and arrays. It tracks descending into - * and ascending out of maps/arrays. It processes breaks that - * terminate indefinite-length maps and arrays. - * - * 3. QCBORDecode_Private_GetNextMapEntry - This handles the combining - * of two items, the label and the data, that make up a map entry. It - * only does work on maps. It combines the label and data items into - * one labeled item. - * - * 4. QCBORDecode_Private_GetNextTagNumber - This decodes type 6 tag - * numbers. It turns the tag numbers into bit flags associated with - * the data item. No actual decoding of the contents of the tag is - * performed here. - * - * 5. QCBORDecode_Private_GetNextFullString - This assembles the - * sub-items that make up an indefinite-length string into one string - * item. It uses the string allocator to create contiguous space for - * the item. It processes all breaks that are part of - * indefinite-length strings. - * - * 6. QCBOR_Private_DecodeAtomicDataItem - This decodes the atomic - * data items in CBOR. Each atomic data item has a "major type", an - * integer "argument" and optionally some content. For text and byte - * strings, the content is the bytes that make up the string. These - * are the smallest data items that are considered to be well-formed. - * The content may also be other data items in the case of aggregate - * types. They are not handled in this layer. - * - * This uses about 350 bytes of stack. This number comes from - * instrumenting (printf address of stack variables) the code on x86 - * compiled for size optimization. - */ - - -/* - * Note about use of int and unsigned variables. - * - * See http://www.unix.org/whitepapers/64bit.html for reasons int is - * used carefully here, and in particular why it isn't used in the - * public interface. Also see - * https://stackoverflow.com/questions/17489857/why-is-int-typically-32-bit-on-64-bit-compilers - * - * Int is used for values that need less than 16-bits and would be - * subject to integer promotion and result in complaining from static - * analyzers. - */ - - -/** - * @brief Decode the CBOR head, the type and argument. - * - * @param[in] pUInBuf The input buffer to read from. - * @param[in] uConfigFlags Decode mode flags. - * @param[out] pnMajorType The decoded major type. - * @param[out] puArgument The decoded argument. - * @param[out] pnAdditionalInfo The decoded Lower 5 bits of initial byte. - * - * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved features - * @retval QCBOR_ERR_HIT_END Unexpected end of input - * - * This decodes the CBOR "head" that every CBOR data item has. See - * longer description in QCBOREncode_EncodeHead(). - * - * This does the network to host byte order conversion. The conversion - * here also provides the conversion for floats in addition to that - * for lengths, tags and integer values. - * - * The int type is preferred to uint8_t for some variables as this - * avoids integer promotions, can reduce code size and makes static - * analyzers happier. - */ -static QCBORError -QCBOR_Private_DecodeHead(UsefulInputBuf *pUInBuf, -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - QCBORDecodeMode uConfigFlags, -#endif - int *pnMajorType, - uint64_t *puArgument, - int *pnAdditionalInfo) -{ - QCBORError uReturn; - uint64_t uArgument; - - /* Get and break down initial byte that every CBOR data item has */ - const int nInitialByte = (int)UsefulInputBuf_GetByte(pUInBuf); - const int nTmpMajorType = nInitialByte >> 5; - const int nAdditionalInfo = nInitialByte & 0x1f; - - if(nAdditionalInfo >= LEN_IS_ONE_BYTE && nAdditionalInfo <= LEN_IS_EIGHT_BYTES) { - /* Need to get 1,2,4 or 8 additional argument bytes. Map - * LEN_IS_ONE_BYTE..LEN_IS_EIGHT_BYTES to actual length. - */ - static const uint8_t aIterate[] = {1,2,4,8}; - - /* Loop getting all the bytes in the argument */ - uArgument = 0; - for(int i = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE]; i; i--) { - /* This shift-and-add gives the endian conversion. */ - uArgument = (uArgument << 8) + UsefulInputBuf_GetByte(pUInBuf); - } - -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - /* If requested, check that argument is in preferred form */ - if(uConfigFlags & QCBOR_DECODE_ONLY_PREFERRED_NUMBERS) { - uint64_t uMinArgument; - - if(nAdditionalInfo == LEN_IS_ONE_BYTE) { - if(uArgument < 24) { - uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE; - goto Done; - } - } else { - if(nTmpMajorType != CBOR_MAJOR_TYPE_SIMPLE) { - /* Check only if not a floating-point number */ - int nArgLen = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE - 1]; - uMinArgument = UINT64_MAX >> ((int)sizeof(uint64_t) - nArgLen) * 8; - if(uArgument <= uMinArgument) { - uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE; - goto Done; - } - } - } - } -#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - - } else if(nAdditionalInfo >= ADDINFO_RESERVED1 && nAdditionalInfo <= ADDINFO_RESERVED3) { - /* The reserved and thus-far unused additional info values */ - uReturn = QCBOR_ERR_UNSUPPORTED; - goto Done; - } else { -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - if(uConfigFlags & QCBOR_DECODE_NO_INDEF_LENGTH && nAdditionalInfo == LEN_IS_INDEFINITE) { - uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE; - goto Done; - } -#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - - /* Less than 24, additional info is argument or 31, an - * indefinite-length. No more bytes to get. - */ - uArgument = (uint64_t)nAdditionalInfo; - } - - if(UsefulInputBuf_GetError(pUInBuf)) { - uReturn = QCBOR_ERR_HIT_END; - goto Done; - } - - /* All successful if arrived here. */ - uReturn = QCBOR_SUCCESS; - *pnMajorType = nTmpMajorType; - *puArgument = uArgument; - *pnAdditionalInfo = nAdditionalInfo; - -Done: - return uReturn; -} - - -/** - * @brief Decode integer types, major types 0 and 1. - * - * @param[in] nMajorType The CBOR major type (0 or 1). - * @param[in] uArgument The argument from the head. - * @param[in] nAdditionalInfo So it can be error-checked. - * @param[out] pDecodedItem The filled in decoded item. - * - * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered. - * @retval QCBOR_ERR_BAD_INT nAdditionalInfo indicated indefinte. - * - * Must only be called when major type is 0 or 1. - * - * CBOR doesn't explicitly specify two's compliment for integers but - * all CPUs use it these days and the test vectors in the RFC are - * so. All integers in encoded CBOR are unsigned and the CBOR major - * type indicates positive or negative. CBOR can express positive - * integers up to 2^64 - 1 negative integers down to -2^64. Note that - * negative numbers can be one more - * away from zero than positive because there is no negative zero. - * - * The "65-bit negs" are values CBOR can encode that can't fit - * into an int64_t or uint64_t. They decoded as a special type - * QCBOR_TYPE_65BIT_NEG_INT. Not that this type does NOT - * take into account the offset of one for CBOR negative integers. - * It must be applied to get the correct value. Applying this offset - * would overflow a uint64_t. - */ -static QCBORError -QCBOR_Private_DecodeInteger(const int nMajorType, - const uint64_t uArgument, - const int nAdditionalInfo, - QCBORItem *pDecodedItem) -{ - QCBORError uReturn = QCBOR_SUCCESS; - - if(nAdditionalInfo == LEN_IS_INDEFINITE) { - uReturn = QCBOR_ERR_BAD_INT; - goto Done; - } - - if(nMajorType == CBOR_MAJOR_TYPE_POSITIVE_INT) { - if (uArgument <= INT64_MAX) { - pDecodedItem->val.int64 = (int64_t)uArgument; - pDecodedItem->uDataType = QCBOR_TYPE_INT64; - - } else { - pDecodedItem->val.uint64 = uArgument; - pDecodedItem->uDataType = QCBOR_TYPE_UINT64; - } - - } else { - if(uArgument <= INT64_MAX) { - /* INT64_MIN is one further away from 0 than INT64_MAX - * so the -1 here doesn't overflow. */ - pDecodedItem->val.int64 = (-(int64_t)uArgument) - 1; - pDecodedItem->uDataType = QCBOR_TYPE_INT64; - - } else { - pDecodedItem->val.uint64 = uArgument; - pDecodedItem->uDataType = QCBOR_TYPE_65BIT_NEG_INT; - } - } - -Done: - return uReturn; -} - - -/** - * @brief Decode text and byte strings - * - * @param[in] pMe Decoder context. - * @param[in] bAllocate Whether to allocate and copy string. - * @param[in] nMajorType Whether it is a byte or text string. - * @param[in] uStrLen The length of the string. - * @param[in] nAdditionalInfo Whether it is an indefinite-length string. - * @param[out] pDecodedItem The filled-in decoded item. - * - * @retval QCBOR_ERR_HIT_END Unexpected end of input. - * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. - * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. - * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Allocation requested, but no allocator - * - * This reads @c uStrlen bytes from the input and fills in @c - * pDecodedItem. If @c bAllocate is true, then memory for the string - * is allocated. - */ -static QCBORError -QCBOR_Private_DecodeString(QCBORDecodeContext *pMe, - const bool bAllocate, - const int nMajorType, - const uint64_t uStrLen, - const int nAdditionalInfo, - QCBORItem *pDecodedItem) -{ - QCBORError uReturn = QCBOR_SUCCESS; - - /* ---- Figure out the major type ---- */ - #if CBOR_MAJOR_TYPE_BYTE_STRING + 4 != QCBOR_TYPE_BYTE_STRING - #error QCBOR_TYPE_BYTE_STRING not lined up with major type - #endif - - #if CBOR_MAJOR_TYPE_TEXT_STRING + 4 != QCBOR_TYPE_TEXT_STRING - #error QCBOR_TYPE_TEXT_STRING not lined up with major type - #endif - pDecodedItem->uDataType = (uint8_t)(nMajorType + 4); - - if(nAdditionalInfo == LEN_IS_INDEFINITE) { - /* --- Just the head of an indefinite-length string --- */ - pDecodedItem->val.string = (UsefulBufC){NULL, QCBOR_STRING_LENGTH_INDEFINITE}; - - } else { - /* --- A definite-length string --- */ - /* --- (which might be a chunk of an indefinte-length string) --- */ - - /* CBOR lengths can be 64 bits, but size_t is not 64 bits on all - * CPUs. This check makes the casts to size_t below safe. - * - * The max is 4 bytes less than the largest sizeof() so this can be - * tested by putting a SIZE_MAX length in the CBOR test input (no - * one will care the limit on strings is 4 bytes shorter). - */ - if(uStrLen > SIZE_MAX-4) { - uReturn = QCBOR_ERR_STRING_TOO_LONG; - goto Done; - } - - const UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(&(pMe->InBuf), (size_t)uStrLen); - if(UsefulBuf_IsNULLC(Bytes)) { - /* Failed to get the bytes for this string item */ - uReturn = QCBOR_ERR_HIT_END; - goto Done; - } - - if(bAllocate) { -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS - /* --- Put string in allocated memory --- */ - - /* Note that this is not where allocation to coalesce - * indefinite-length strings is done. This is for when the - * caller has requested all strings be allocated. Disabling - * indefinite length strings also disables this allocate-all - * option. - */ - - if(pMe->StringAllocator.pfAllocator == NULL) { - uReturn = QCBOR_ERR_NO_STRING_ALLOCATOR; - goto Done; - } - UsefulBuf NewMem = StringAllocator_Allocate(&(pMe->StringAllocator), (size_t)uStrLen); - if(UsefulBuf_IsNULL(NewMem)) { - uReturn = QCBOR_ERR_STRING_ALLOCATE; - goto Done; - } - pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes); - pDecodedItem->uDataAlloc = 1; -#else - uReturn = QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED; -#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ - } else { - /* --- Normal case with no string allocator --- */ - pDecodedItem->val.string = Bytes; - } - } - -Done: - return uReturn; -} - - -/** - * @brief Decode array or map. - * - * @param[in] uConfigFlags Decoder mode. - * @param[in] nMajorType Whether it is a byte or text string. - * @param[in] uItemCount The length of the string. - * @param[in] nAdditionalInfo Whether it is an indefinite-length. - * @param[out] pDecodedItem The filled-in decoded item. - * - * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinites disabled. - * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array/map. - * - * Not much to do for arrays and maps. Just the type item count (but a - * little messy because of ifdefs for indefinite-lengths and - * map-as-array decoding). - * - * This also does the bulk of the work for @ref - * QCBOR_DECODE_MODE_MAP_AS_ARRAY, a special mode to handle - * arbitrarily complex map labels. This ifdefs out with - * QCBOR_DISABLE_NON_INTEGER_LABELS. - */ -static QCBORError -QCBOR_Private_DecodeArrayOrMap(const QCBORDecodeMode uConfigFlags, - const int nMajorType, - uint64_t uItemCount, - const int nAdditionalInfo, - QCBORItem *pDecodedItem) -{ - QCBORError uReturn; - - /* ------ Sort out the data type ------ */ - #if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY - #error QCBOR_TYPE_ARRAY value not lined up with major type - #endif - - #if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP - #error QCBOR_TYPE_MAP value not lined up with major type - #endif - pDecodedItem->uDataType = (uint8_t)nMajorType; -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - if((uConfigFlags & QCBOR_DECODE_MODE_MAP_AS_ARRAY) && nMajorType == QCBOR_TYPE_MAP) { - pDecodedItem->uDataType = QCBOR_TYPE_MAP_AS_ARRAY; - } -#else - (void)uConfigFlags; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - - uReturn = QCBOR_SUCCESS; - - if(nAdditionalInfo == LEN_IS_INDEFINITE) { - /* ------ Indefinite-length array/map ----- */ -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS - pDecodedItem->val.uCount = QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH; -#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - uReturn = QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED; -#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - } else { - /* ----- Definite-length array/map ----- */ - if(uItemCount > (nMajorType == QCBOR_TYPE_MAP ? QCBOR_MAX_ITEMS_IN_MAP : QCBOR_MAX_ITEMS_IN_ARRAY)) { - uReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG; - - } else { -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - if((uConfigFlags & QCBOR_DECODE_MODE_MAP_AS_ARRAY) && nMajorType == QCBOR_TYPE_MAP) { - /* ------ Map as array ------ */ - uItemCount *= 2; - } -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - - /* cast OK because of check above */ - pDecodedItem->val.uCount = (uint16_t)uItemCount; - } - } - - return uReturn; -} - - -/** - * @brief Decode a tag number. - * - * @param[in] uTagNumber The length of the string. - * @param[in] nAdditionalInfo So this can be error-checked. - * @param[out] pDecodedItem The filled-in decoded item. - * - * @retval QCBOR_ERR_BAD_INT nAdditionalInfo is LEN_IS_INDEFINITE. - * @retval QCBOR_ERR_TAGS_DISABLED QCBOR_DISABLE_TAGS is defined. - * - * Not much to do for tags, but fill in pDecodedItem and check for - * error in nAdditionalInfo. - */ -static QCBORError -QCBOR_Private_DecodeTagNumber(const uint64_t uTagNumber, - const int nAdditionalInfo, - QCBORItem *pDecodedItem) -{ -#ifndef QCBOR_DISABLE_TAGS - if(nAdditionalInfo == LEN_IS_INDEFINITE) { - return QCBOR_ERR_BAD_INT; - } else { - pDecodedItem->val.uTagNumber = uTagNumber; - pDecodedItem->uDataType = QCBOR_TYPE_TAG_NUMBER; - return QCBOR_SUCCESS; - } -#else /* QCBOR_DISABLE_TAGS */ - (void)nAdditionalInfo; - (void)uTagNumber; - (void)pDecodedItem; - return QCBOR_ERR_TAGS_DISABLED; -#endif /* QCBOR_DISABLE_TAGS */ -} - - -#ifndef USEFULBUF_DISABLE_ALL_FLOAT - -#if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT) - -static QCBORError -QCBORDecode_Private_HalfConformance(const double d, const QCBORDecodeMode uConfigFlags) -{ - struct IEEE754_ToInt ToInt; - - /* Only need to check for conversion to integer because - * half-precision is always preferred serialization. Don't - * need special checker for half-precision because whole - * numbers always convert perfectly from half to double. - * - * This catches half-precision with NaN payload too. - * - * The only thing allowed here is a double/half-precision that - * can't be converted to anything but a double. - */ - if(uConfigFlags & QCBOR_DECODE_ONLY_REDUCED_FLOATS) { - ToInt = IEEE754_DoubleToInt(d); - if(ToInt.type != QCBOR_TYPE_DOUBLE) { - return QCBOR_ERR_DCBOR_CONFORMANCE; - } - } - - return QCBOR_SUCCESS; -} - - -static QCBORError -QCBORDecode_Private_SingleConformance(const float f, const QCBORDecodeMode uconfigFlags) -{ - struct IEEE754_ToInt ToInt; - IEEE754_union ToSmaller; - - if(uconfigFlags & QCBOR_DECODE_ONLY_REDUCED_FLOATS) { - /* See if it could have been encoded as an integer */ - ToInt = IEEE754_SingleToInt(f); - if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) { - return QCBOR_ERR_DCBOR_CONFORMANCE; - } - - /* Make sure there is no NaN payload */ - if(IEEE754_SingleHasNaNPayload(f)) { - return QCBOR_ERR_DCBOR_CONFORMANCE; - } - } - - /* See if it could have been encoded shorter */ - if(uconfigFlags & QCBOR_DECODE_ONLY_PREFERRED_NUMBERS) { - ToSmaller = IEEE754_SingleToHalf(f, true); - if(ToSmaller.uSize != sizeof(float)) { - return QCBOR_ERR_PREFERRED_CONFORMANCE; - } - } - - return QCBOR_SUCCESS; -} - - -static QCBORError -QCBORDecode_Private_DoubleConformance(const double d, QCBORDecodeMode uConfigFlags) -{ - struct IEEE754_ToInt ToInt; - IEEE754_union ToSmaller; - - if(uConfigFlags & QCBOR_DECODE_ONLY_REDUCED_FLOATS) { - /* See if it could have been encoded as an integer */ - ToInt = IEEE754_DoubleToInt(d); - if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) { - return QCBOR_ERR_DCBOR_CONFORMANCE; - } - /* Make sure there is no NaN payload */ - if(IEEE754_DoubleHasNaNPayload(d)) { - return QCBOR_ERR_DCBOR_CONFORMANCE; - } - } - - /* See if it could have been encoded shorter */ - if(uConfigFlags & QCBOR_DECODE_ONLY_PREFERRED_NUMBERS) { - ToSmaller = IEEE754_DoubleToSmaller(d, true, true); - if(ToSmaller.uSize != sizeof(double)) { - return QCBOR_ERR_PREFERRED_CONFORMANCE; - } - } - - return QCBOR_SUCCESS; -} -#else /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */ - -static QCBORError -QCBORDecode_Private_SingleConformance(const float f, const QCBORDecodeMode uConfigFlags) -{ - (void)f; - if(uConfigFlags & (QCBOR_DECODE_ONLY_REDUCED_FLOATS | QCBOR_DECODE_ONLY_PREFERRED_NUMBERS)) { - return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE; - } else { - return QCBOR_SUCCESS; - } -} - -static QCBORError -QCBORDecode_Private_DoubleConformance(const double d, const QCBORDecodeMode uConfigFlags) -{ - (void)d; - if(uConfigFlags & (QCBOR_DECODE_ONLY_REDUCED_FLOATS | QCBOR_DECODE_ONLY_PREFERRED_NUMBERS)) { - return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE; - } else { - return QCBOR_SUCCESS; - } -} -#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */ - - -/* - * Decode a float - */ -static QCBORError -QCBOR_Private_DecodeFloat(const QCBORDecodeMode uConfigFlags, - const int nAdditionalInfo, - const uint64_t uArgument, - QCBORItem *pDecodedItem) -{ - QCBORError uReturn = QCBOR_SUCCESS; - float single; - - (void)single; /* Avoid unused error from various #ifndefs */ - - switch(nAdditionalInfo) { - case HALF_PREC_FLOAT: /* 25 */ -#ifndef QCBOR_DISABLE_PREFERRED_FLOAT - /* Half-precision is returned as a double. The cast to - * uint16_t is safe because the encoded value was 16 bits. It - * was widened to 64 bits to be passed in here. - */ - pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument); - pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE; - - uReturn = QCBORDecode_Private_HalfConformance(pDecodedItem->val.dfnum, uConfigFlags); - if(uReturn != QCBOR_SUCCESS) { - break; - } -#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */ - uReturn = FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS); - break; - - case SINGLE_PREC_FLOAT: /* 26 */ - /* Single precision is normally returned as a double since - * double is widely supported, there is no loss of precision, - * it makes it easy for the caller in most cases and it can - * be converted back to single with no loss of precision - * - * The cast to uint32_t is safe because the encoded value was - * 32 bits. It was widened to 64 bits to be passed in here. - */ - single = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument); - uReturn = QCBORDecode_Private_SingleConformance(single, uConfigFlags); - if(uReturn != QCBOR_SUCCESS) { - break; - } - -#ifndef QCBOR_DISABLE_FLOAT_HW_USE - /* In the normal case, use HW to convert float to - * double. */ - pDecodedItem->val.dfnum = (double)single; - pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE; -#else /* QCBOR_DISABLE_FLOAT_HW_USE */ - /* Use of float HW is disabled, return as a float. */ - pDecodedItem->val.fnum = single; - pDecodedItem->uDataType = QCBOR_TYPE_FLOAT; - - /* IEEE754_FloatToDouble() could be used here to return as - * a double, but it adds object code and most likely - * anyone disabling FLOAT HW use doesn't care about floats - * and wants to save object code. - */ -#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */ - uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS); - break; - - case DOUBLE_PREC_FLOAT: /* 27 */ - pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument); - pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE; - - uReturn = QCBORDecode_Private_DoubleConformance(pDecodedItem->val.dfnum, uConfigFlags); - if(uReturn != QCBOR_SUCCESS) { - break; - } - uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS); - break; - } - - return uReturn; -} -#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */ - - - -/* Make sure #define value line up as DecodeSimple counts on this. */ -#if QCBOR_TYPE_FALSE != CBOR_SIMPLEV_FALSE -#error QCBOR_TYPE_FALSE macro value wrong -#endif - -#if QCBOR_TYPE_TRUE != CBOR_SIMPLEV_TRUE -#error QCBOR_TYPE_TRUE macro value wrong -#endif - -#if QCBOR_TYPE_NULL != CBOR_SIMPLEV_NULL -#error QCBOR_TYPE_NULL macro value wrong -#endif - -#if QCBOR_TYPE_UNDEF != CBOR_SIMPLEV_UNDEF -#error QCBOR_TYPE_UNDEF macro value wrong -#endif - -#if QCBOR_TYPE_BREAK != CBOR_SIMPLE_BREAK -#error QCBOR_TYPE_BREAK macro value wrong -#endif - -#if QCBOR_TYPE_DOUBLE != DOUBLE_PREC_FLOAT -#error QCBOR_TYPE_DOUBLE macro value wrong -#endif - -#if QCBOR_TYPE_FLOAT != SINGLE_PREC_FLOAT -#error QCBOR_TYPE_FLOAT macro value wrong -#endif - -/** - * @brief Decode major type 7 -- true, false, floating-point, break... - * - * @param[in] nAdditionalInfo The lower five bits from the initial byte. - * @param[in] uArgument The argument from the head. - * @param[out] pDecodedItem The filled in decoded item. - * - * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode - * of half-precision disabled - * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all float - * decode is disabled. - * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of simple - * type in input. - */ -static QCBORError -QCBOR_Private_DecodeType7(const QCBORDecodeMode uConfigFlags, - const int nAdditionalInfo, - const uint64_t uArgument, - QCBORItem *pDecodedItem) -{ - QCBORError uReturn = QCBOR_SUCCESS; - - /* uAdditionalInfo is 5 bits from the initial byte. Compile time - * checks above make sure uAdditionalInfo values line up with - * uDataType values. DecodeHead() never returns an AdditionalInfo - * > 0x1f so cast is safe. - */ - pDecodedItem->uDataType = (uint8_t)nAdditionalInfo; - - switch(nAdditionalInfo) { - /* No check for ADDINFO_RESERVED1 - ADDINFO_RESERVED3 as they - * are caught before this is called. - */ - - case HALF_PREC_FLOAT: /* 25 */ - case SINGLE_PREC_FLOAT: /* 26 */ - case DOUBLE_PREC_FLOAT: /* 27 */ -#ifndef USEFULBUF_DISABLE_ALL_FLOAT - uReturn = QCBOR_Private_DecodeFloat(uConfigFlags, nAdditionalInfo, uArgument, pDecodedItem); -#else - uReturn = QCBOR_ERR_ALL_FLOAT_DISABLED; -#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */ - break; - - case CBOR_SIMPLEV_FALSE: /* 20 */ - case CBOR_SIMPLEV_TRUE: /* 21 */ - case CBOR_SIMPLEV_NULL: /* 22 */ - case CBOR_SIMPLEV_UNDEF: /* 23 */ - case CBOR_SIMPLE_BREAK: /* 31 */ -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - if((uConfigFlags & QCBOR_DECODE_DISALLOW_DCBOR_SIMPLES) && - nAdditionalInfo == CBOR_SIMPLEV_UNDEF) { - uReturn = QCBOR_ERR_DCBOR_CONFORMANCE; - goto Done; - } -#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - break; /* nothing to do */ - - case CBOR_SIMPLEV_ONEBYTE: /* 24 */ - if(uArgument <= CBOR_SIMPLE_BREAK) { - /* This takes out f8 00 ... f8 1f which should be encoded - * as e0 … f7 -- preferred serialization check for simple values. - */ - uReturn = QCBOR_ERR_BAD_TYPE_7; - goto Done; - } - /* FALLTHROUGH */ - - default: /* 0-19 */ -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - if((uConfigFlags & QCBOR_DECODE_DISALLOW_DCBOR_SIMPLES) && - (uArgument < CBOR_SIMPLEV_FALSE || uArgument > CBOR_SIMPLEV_NULL)) { - uReturn = QCBOR_ERR_DCBOR_CONFORMANCE; - goto Done; - } -#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */ - - pDecodedItem->uDataType = QCBOR_TYPE_UKNOWN_SIMPLE; - /* QCBOR_Private_DecodeHead() will make uArgument equal to - * nAdditionalInfo when nAdditionalInfo is < 24. This cast is - * safe because the 2, 4 and 8 byte lengths of uNumber are in - * the double/float cases above - */ - pDecodedItem->val.uSimple = (uint8_t)uArgument; - break; - } - -Done: - return uReturn; -} - - -/** - * @brief Decode a single primitive data item (decode layer 6). - * - * @param[in] pMe Decoder context. - * @param[in] bAllocateStrings If true, use allocator for strings. - * @param[out] pDecodedItem The filled-in decoded item. - * - * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved - * features - * @retval QCBOR_ERR_HIT_END Unexpected end of input - * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered - * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. - * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. - * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Allocation requested, but no allocator - * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode - * of half-precision disabled - * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all - * float decode is disabled. - * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of - * simple type in input. - * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array - * in input, but indefinite - * lengths disabled. - * @retval QCBOR_ERR_BAD_INT nAdditionalInfo indicated indefinte. - * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array/map. - * @retval QCBOR_ERR_TAGS_DISABLED QCBOR_DISABLE_TAGS is defined. - * - * This decodes the most primitive/atomic data item. It does no - * combining of data items. - */ -static QCBORError -QCBOR_Private_DecodeAtomicDataItem(QCBORDecodeContext *pMe, - const bool bAllocateStrings, - QCBORItem *pDecodedItem) -{ - QCBORError uReturn; - int nMajorType = 0; - uint64_t uArgument = 0; - int nAdditionalInfo = 0; - - const QCBORDecodeMode uDecodeMode = pMe->uDecodeMode; - - memset(pDecodedItem, 0, sizeof(QCBORItem)); - - /* Decode the "head" that every CBOR item has into the major type, - * argument and the additional info. - */ - uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf), -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - // TODO: make this prettier; will optimizer take out stuff without ifdef? - uDecodeMode, -#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */ - &nMajorType, - &uArgument, - &nAdditionalInfo); - - if(uReturn != QCBOR_SUCCESS) { - return uReturn; - } - - /* All the functions below get inlined by the optimizer. This code - * is easier to read with them all being similar functions, even if - * some functions don't do much. - */ - switch (nMajorType) { - case CBOR_MAJOR_TYPE_POSITIVE_INT: /* Major type 0 */ - case CBOR_MAJOR_TYPE_NEGATIVE_INT: /* Major type 1 */ - return QCBOR_Private_DecodeInteger(nMajorType, uArgument, nAdditionalInfo, pDecodedItem); - break; - - case CBOR_MAJOR_TYPE_BYTE_STRING: /* Major type 2 */ - case CBOR_MAJOR_TYPE_TEXT_STRING: /* Major type 3 */ - return QCBOR_Private_DecodeString(pMe, bAllocateStrings, nMajorType, uArgument, nAdditionalInfo, pDecodedItem); - break; - - case CBOR_MAJOR_TYPE_ARRAY: /* Major type 4 */ - case CBOR_MAJOR_TYPE_MAP: /* Major type 5 */ - return QCBOR_Private_DecodeArrayOrMap(uDecodeMode, nMajorType, uArgument, nAdditionalInfo, pDecodedItem); - break; - - case CBOR_MAJOR_TYPE_TAG: /* Major type 6, tag numbers */ - return QCBOR_Private_DecodeTagNumber(uArgument, nAdditionalInfo, pDecodedItem); - break; - - case CBOR_MAJOR_TYPE_SIMPLE: - /* Major type 7: float, double, true, false, null... */ - return QCBOR_Private_DecodeType7(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem); - break; - - default: - /* Never happens because DecodeHead() should never return > 7 */ - return QCBOR_ERR_UNSUPPORTED; - break; - } -} - - -/** - * @brief Process indefinite-length strings (decode layer 5). - * - * @param[in] pMe Decoder context - * @param[out] pDecodedItem The decoded item that work is done on. - * - * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved - * features - * @retval QCBOR_ERR_HIT_END Unexpected end of input - * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered - * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. - * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. - * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode - * of half-precision disabled - * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all - * float decode is disabled. - * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of - * simple type in input. - * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array - * in input, but indefinite - * lengths disabled. - * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, - * but no string allocator. - * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. - * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in - * input, but indefinite-length - * strings are disabled. - * - * If @c pDecodedItem is not an indefinite-length string, this does nothing. - * - * If it is, this loops getting the subsequent chunk data items that - * make up the string. The string allocator is used to make a - * contiguous buffer for the chunks. When this completes @c - * pDecodedItem contains the put-together string. - * - * Code Reviewers: THIS FUNCTION DOES A LITTLE POINTER MATH - */ -static QCBORError -QCBORDecode_Private_GetNextFullString(QCBORDecodeContext *pMe, - QCBORItem *pDecodedItem) -{ - /* Aproximate stack usage - * 64-bit 32-bit - * local vars 32 16 - * 2 UsefulBufs 32 16 - * QCBORItem 56 52 - * TOTAL 120 74 - */ - QCBORError uReturn; - - /* A note about string allocation -- Memory for strings is - * allocated either because 1) indefinte-length string chunks are - * being coalecsed or 2) caller has requested all strings be - * allocated. The first case is handed below here. The second case - * is handled in DecodeString if the bAllocate is true. That - * boolean originates here with pMe->bStringAllocateAll immediately - * below. That is, QCBOR_Private_DecodeAtomicDataItem() is called - * in two different contexts here 1) main-line processing which is - * where definite-length strings need to be allocated if - * bStringAllocateAll is true and 2) processing chunks of - * indefinite-lengths strings in in which case there must be no - * allocation. - */ - - - uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, pMe->bStringAllocateAll, pDecodedItem); - if(uReturn != QCBOR_SUCCESS) { - goto Done; - } - - /* This is where out-of-place break is detected for the whole - * decoding stack. Break is an error for everything that calls - * QCBORDecode_Private_GetNextFullString(), so the check is - * centralized here. - */ - if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) { - uReturn = QCBOR_ERR_BAD_BREAK; - goto Done; - } - - - /* Skip out if not an indefinite-length string */ - const uint8_t uStringType = pDecodedItem->uDataType; - if(uStringType != QCBOR_TYPE_BYTE_STRING && - uStringType != QCBOR_TYPE_TEXT_STRING) { - goto Done; - } - if(pDecodedItem->val.string.len != QCBOR_STRING_LENGTH_INDEFINITE) { - goto Done; - } - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS - /* Can't decode indefinite-length strings without a string allocator */ - if(!pMe->StringAllocator.pfAllocator) { - uReturn = QCBOR_ERR_NO_STRING_ALLOCATOR; - goto Done; - } - - /* Loop getting chunks of the indefinite-length string */ - UsefulBufC FullString = NULLUsefulBufC; - - for(;;) { - /* Get QCBORItem for next chunk */ - QCBORItem StringChunkItem; - /* Pass false to DecodeAtomicDataItem() because the individual - * string chunks in an indefinite-length must not be - * allocated. They are always copied into the allocated - * contiguous buffer allocated here. - */ - uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, false, &StringChunkItem); - if(uReturn) { - break; - } - - /* Is item is the marker for end of the indefinite-length string? */ - if(StringChunkItem.uDataType == QCBOR_TYPE_BREAK) { - /* String is complete */ - pDecodedItem->val.string = FullString; - pDecodedItem->uDataAlloc = 1; - break; - } - - /* All chunks must be of the same type, the type of the item - * that introduces the indefinite-length string. This also - * catches errors where the chunk is not a string at all and an - * indefinite-length string inside an indefinite-length string. - */ - if(StringChunkItem.uDataType != uStringType || - StringChunkItem.val.string.len == QCBOR_STRING_LENGTH_INDEFINITE) { - uReturn = QCBOR_ERR_INDEFINITE_STRING_CHUNK; - break; - } - - if (StringChunkItem.val.string.len > 0) { - /* The first time throurgh FullString.ptr is NULL and this is - * equivalent to StringAllocator_Allocate(). Subsequently it is - * not NULL and a reallocation happens. - */ - UsefulBuf NewMem = StringAllocator_Reallocate(&(pMe->StringAllocator), - FullString.ptr, - FullString.len + StringChunkItem.val.string.len); - if(UsefulBuf_IsNULL(NewMem)) { - uReturn = QCBOR_ERR_STRING_ALLOCATE; - break; - } - - /* Copy new string chunk to the end of accumulated string */ - FullString = UsefulBuf_CopyOffset(NewMem, FullString.len, StringChunkItem.val.string); - } - } - - if(uReturn != QCBOR_SUCCESS && !UsefulBuf_IsNULLC(FullString)) { - /* Getting the item failed, clean up the allocated memory */ - StringAllocator_Free(&(pMe->StringAllocator), FullString.ptr); - } -#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ - uReturn = QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED; -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ - -Done: - return uReturn; -} - - -#ifndef QCBOR_DISABLE_TAGS -/** - * @brief This converts a tag number to a shorter mapped value for storage. - * - * @param[in] pMe The decode context. - * @param[in] uUnMappedTag The tag number to map - * @param[out] puMappedTagNumber The stored tag number. - * - * @return error code. - * - * The main point of mapping tag numbers is make QCBORItem - * smaller. With this mapping storage of 4 tags takes up 8 - * bytes. Without, it would take up 32 bytes. - * - * This maps tag numbers greater than QCBOR_LAST_UNMAPPED_TAG. - * QCBOR_LAST_UNMAPPED_TAG is a little smaller than MAX_UINT16. - * - * See also UnMapTagNumber() and @ref QCBORItem. - */ -static QCBORError -QCBORDecode_Private_MapTagNumber(QCBORDecodeContext *pMe, - const uint64_t uUnMappedTag, - uint16_t *puMappedTagNumber) -{ - if(uUnMappedTag > QCBOR_LAST_UNMAPPED_TAG) { - unsigned uTagMapIndex; - /* Is there room in the tag map, or is it in it already? */ - for(uTagMapIndex = 0; uTagMapIndex < QCBOR_NUM_MAPPED_TAGS; uTagMapIndex++) { - if(pMe->auMappedTags[uTagMapIndex] == CBOR_TAG_INVALID64) { - break; - } - if(pMe->auMappedTags[uTagMapIndex] == uUnMappedTag) { - break; - } - } - if(uTagMapIndex >= QCBOR_NUM_MAPPED_TAGS) { - return QCBOR_ERR_TOO_MANY_TAGS; - } - - /* Covers the cases where tag is new and were it is already in the map */ - pMe->auMappedTags[uTagMapIndex] = uUnMappedTag; - *puMappedTagNumber = (uint16_t)(uTagMapIndex + QCBOR_LAST_UNMAPPED_TAG + 1); - - } else { - *puMappedTagNumber = (uint16_t)uUnMappedTag; - } - - return QCBOR_SUCCESS; -} - - -/** - * @brief This converts a mapped tag number to the actual tag number. - * - * @param[in] pMe The decode context. - * @param[in] uMappedTagNumber The stored tag number. - * - * @return The actual tag number is returned or - * @ref CBOR_TAG_INVALID64 on error. - * - * This is the reverse of MapTagNumber() - */ -static uint64_t -QCBORDecode_Private_UnMapTagNumber(const QCBORDecodeContext *pMe, - const uint16_t uMappedTagNumber) -{ - if(uMappedTagNumber <= QCBOR_LAST_UNMAPPED_TAG) { - return uMappedTagNumber; - } else if(uMappedTagNumber == CBOR_TAG_INVALID16) { - return CBOR_TAG_INVALID64; - } else { - /* This won't be negative because of code below in - * MapTagNumber() - */ - const unsigned uIndex = uMappedTagNumber - (QCBOR_LAST_UNMAPPED_TAG + 1); - return pMe->auMappedTags[uIndex]; - } -} - - -static const struct QCBORTagDecoderEntry * -QCBORDecode_Private_LookUpTagDecoder(const struct QCBORTagDecoderEntry *pTagContentTable, - const uint64_t uTagNumber) -{ - const struct QCBORTagDecoderEntry *pTE; - - if(pTagContentTable == NULL) { - return NULL; - } - - for(pTE = pTagContentTable; pTE->uTagNumber != CBOR_TAG_INVALID64; pTE++) { - if(pTE->uTagNumber == uTagNumber || pTE->uTagNumber == CBOR_TAG_ANY) { - break; - } - } - - if(pTE->uTagNumber == CBOR_TAG_INVALID64) { - return NULL; - } - - return pTE; -} -#endif /* ! QCBOR_DISABLE_TAGS */ - - -/** - * @brief Aggregate all tags wrapping a data item (decode layer 4). - * - * @param[in] pMe Decoder context - * @param[out] pDecodedItem The decoded item that work is done on. - * - * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved - * features - * @retval QCBOR_ERR_HIT_END Unexpected end of input - * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered - * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. - * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. - * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode - * of half-precision disabled - * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all - * float decode is disabled. - * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of - * simple type in input. - * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array - * in input, but indefinite - * lengths disabled. - * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, - * but no string allocator. - * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. - * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in - * input, but indefinite-length - * strings are disabled. - * @retval QCBOR_ERR_TOO_MANY_TAGS Too many tag numbers on item. - * - * This loops getting atomic data items until one is not a tag - * number. Usually this is largely pass-through because most - * item are not tag numbers. - */ -static QCBORError -QCBORDecode_Private_GetNextTagNumber(QCBORDecodeContext *pMe, - QCBORItem *pDecodedItem) -{ -#ifndef QCBOR_DISABLE_TAGS - int nIndex; - QCBORError uErr; - uint16_t uMappedTagNumber; - QCBORError uReturn; - - /* Accummulate the tag numbers from multiple items here and then - * copy them into the last item, the non-tag-number item. - */ - QCBORMappedTagNumbers auTagNumbers;; - - /* Initialize to CBOR_TAG_INVALID16 */ - #if CBOR_TAG_INVALID16 != 0xffff - /* Be sure the memset is initializing to CBOR_TAG_INVALID16 */ - #err CBOR_TAG_INVALID16 tag not defined as expected - #endif - memset(auTagNumbers, 0xff, sizeof(auTagNumbers)); - - /* Loop fetching data items until the item fetched is not a tag number */ - uReturn = QCBOR_SUCCESS; - for(nIndex = 0; ; nIndex++) { - uErr = QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem); - if(uErr != QCBOR_SUCCESS) { - uReturn = uErr; - break; - } - - if(pDecodedItem->uDataType != QCBOR_TYPE_TAG_NUMBER) { - /* Successful exit from loop; maybe got some tags, maybe not */ - memcpy(pDecodedItem->auTagNumbers, auTagNumbers, sizeof(auTagNumbers)); - break; - } - - if(nIndex >= QCBOR_MAX_TAGS_PER_ITEM) { - /* No room in the item's tag number array */ - uReturn = QCBOR_ERR_TOO_MANY_TAGS; - /* Continue on to get all tag numbers wrapping this item even - * though it is erroring out in the end. This allows decoding - * to continue. This is a QCBOR resource limit error, not a - * problem with being well-formed CBOR. - */ - continue; - } - - /* Map the tag number */ - uMappedTagNumber = 0; - uReturn = QCBORDecode_Private_MapTagNumber(pMe, pDecodedItem->val.uTagNumber, &uMappedTagNumber); - /* Continue even on error so as to consume all tag numbers - * wrapping this data item so decoding can go on. If - * QCBORDecode_Private_MapTagNumber() errors once it will - * continue to error. - */ - - auTagNumbers[nIndex] = uMappedTagNumber; - } - - return uReturn; - -#else /* ! QCBOR_DISABLE_TAGS */ - - return QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem); - -#endif /* ! QCBOR_DISABLE_TAGS */ -} - - -/** - * @brief Combine a map entry label and value into one item (decode layer 3). - * - * @param[in] pMe Decoder context - * @param[out] pDecodedItem The decoded item that work is done on. - * - * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved - * features - * @retval QCBOR_ERR_HIT_END Unexpected end of input - * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered - * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. - * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. - * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode - * of half-precision disabled - * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all - * float decode is disabled. - * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of - * simple type in input. - * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array - * in input, but indefinite - * lengths disabled. - * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, - * but no string allocator. - * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. - * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in - * input, but indefinite-length - * strings are disabled. - * @retval QCBOR_ERR_TOO_MANY_TAGS Too many tag numbers on item. - * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array. - * @retval QCBOR_ERR_MAP_LABEL_TYPE Map label not string or integer. - * - * If the current nesting level is a map, then this combines pairs of - * items into one data item with a label and value. - * - * This is passthrough if the current nesting level is not a map. - * - * This also implements maps-as-array mode where a map is treated like - * an array to allow caller to do their own label processing. - */ -static QCBORError -QCBORDecode_Private_GetNextMapEntry(QCBORDecodeContext *pMe, - QCBORItem *pDecodedItem, - uint32_t *puLabelEndOffset) -{ - QCBORItem LabelItem; - QCBORError uErr, uErr2; - - uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem); - if(QCBORDecode_IsUnrecoverableError(uErr)) { - goto Done; - } - - if(!DecodeNesting_IsCurrentTypeMap(&(pMe->nesting))) { - /* Not decoding a map. Nothing to do. */ - /* When decoding maps-as-arrays, the type will be - * QCBOR_TYPE_MAP_AS_ARRAY and this function will exit - * here. This is now map processing for maps-as-arrays is not - * done. */ - goto Done; - } - - /* Decoding a map entry, so the item decoded above was the label */ - LabelItem = *pDecodedItem; - - if(puLabelEndOffset != NULL) { - /* Cast is OK because lengths are all 32-bit in QCBOR */ - *puLabelEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf)); - } - - /* Get the value of the map item */ - uErr2 = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem); - if(QCBORDecode_IsUnrecoverableError(uErr2)) { - uErr = uErr2; - goto Done; - } - if(uErr2 != QCBOR_SUCCESS) { - /* The recoverable error for the value overrides the recoverable - * error for the label, if there was an error for the label */ - uErr = uErr2; - } - - /* Combine the label item and value item into one */ - pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc; - pDecodedItem->uLabelType = LabelItem.uDataType; - -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - /* TODO: QCBOR_DECODE_MODE_MAP_STRINGS_ONLY might have been a bad idea. Maybe - * get rid of it in QCBOR 2.0 - */ - if(pMe->uDecodeMode & QCBOR_DECODE_MODE_MAP_STRINGS_ONLY && - LabelItem.uDataType != QCBOR_TYPE_TEXT_STRING) { - uErr = QCBOR_ERR_MAP_LABEL_TYPE; - goto Done; - } -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - - switch(LabelItem.uDataType) { - case QCBOR_TYPE_INT64: - pDecodedItem->label.int64 = LabelItem.val.int64; - break; - -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - case QCBOR_TYPE_UINT64: - pDecodedItem->label.uint64 = LabelItem.val.uint64; - break; - - case QCBOR_TYPE_TEXT_STRING: - case QCBOR_TYPE_BYTE_STRING: - pDecodedItem->label.string = LabelItem.val.string; - break; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - - default: - /* It is possible to skip over labels that are non-aggregate - * types like floats, but not to skip over labels that are - * arrays or maps. We might eventually handle more label - * types like floats as they are not too hard and we now - * have QCBOR_DISABLE_NON_INTEGER_LABELS */ - if(!pMe->bAllowAllLabels || QCBORItem_IsMapOrArray(LabelItem)) { - uErr = QCBOR_ERR_MAP_LABEL_TYPE; - goto Done; - } - } - -Done: - return uErr; -} - - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS -/** - * @brief Peek and see if next data item is a break; - * - * param[in] pUIB UsefulInputBuf to read from. - * @param[out] pbNextIsBreak Indicate if next was a break or not. - * - * @return Any decoding error. - * - * See if next item is a CBOR break. If it is, it is consumed, - * if not it is not consumed. -*/ -static QCBORError -QCBOR_Private_NextIsBreak(QCBORDecodeContext *pMe, bool *pbNextIsBreak) -{ - *pbNextIsBreak = false; - if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf)) != 0) { - QCBORItem Peek; - size_t uPeek = UsefulInputBuf_Tell(&(pMe->InBuf)); - QCBORError uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, false, &Peek); - if(uReturn != QCBOR_SUCCESS) { - return uReturn; - } - if(Peek.uDataType != QCBOR_TYPE_BREAK) { - /* It is not a break, rewind so it can be processed normally. */ - UsefulInputBuf_Seek(&(pMe->InBuf), uPeek); - } else { - *pbNextIsBreak = true; - } - } - - return QCBOR_SUCCESS; -} -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - - -/** - * @brief Ascend up nesting levels if all items in them have been consumed. - * - * @param[in] pMe The decode context. - * @param[in] bMarkEnd If true mark end of maps/arrays with count of zero. - * @param[out] pbBreak Set to true if extra break was consumed. - * - * An item was just consumed, now figure out if it was the - * end of an array/map map that can be closed out. That - * may in turn close out the above array/map... - * - * When ascending indefinite-length arrays and maps, this will correctly - * consume the break for the level above. This is a problem for the - * implementation of QCBORDecode_GetArray() that must not return - * that break. @c pbBreak is set to true to indicate that one - * byte should be removed. - * - * Improvement: this could reduced further if indef is disabled - */ -static QCBORError -QCBORDecode_Private_NestLevelAscender(QCBORDecodeContext *pMe, bool bMarkEnd, bool *pbBreak) -{ - QCBORError uReturn; - - /* Loop ascending nesting levels as long as there is ascending to do */ - while(!DecodeNesting_IsCurrentAtTop(&(pMe->nesting))) { - if(pbBreak) { - *pbBreak = false; - } - - if(DecodeNesting_IsCurrentBstrWrapped(&(pMe->nesting))) { - /* Nesting level is bstr-wrapped CBOR */ - - /* Ascent for bstr-wrapped CBOR is always by explicit call - * so no further ascending can happen. - */ - break; - - } else if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) { - /* Level is a definite-length array/map */ - - /* Decrement the item count the definite-length array/map */ - DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(&(pMe->nesting)); - if(!DecodeNesting_IsEndOfDefiniteLengthMapOrArray(&(pMe->nesting))) { - /* Didn't close out array/map, so all work here is done */ - break; - } - /* All items in a definite-length array were consumed so it - * is time to ascend one level. This happens below. - */ - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS - } else { - /* Level is an indefinite-length array/map. */ - - /* Check for a break which is what ends indefinite-length arrays/maps */ - bool bIsBreak = false; - uReturn = QCBOR_Private_NextIsBreak(pMe, &bIsBreak); - if(uReturn != QCBOR_SUCCESS) { - goto Done; - } - - if(!bIsBreak) { - /* Not a break so array/map does not close out. All work is done */ - break; - } - - /* It was a break in an indefinitelength map / array so - * it is time to ascend one level. - */ - if(pbBreak) { - *pbBreak = true; - } - -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - } - - - /* All items in the array/map have been consumed. */ - - /* But ascent in bounded mode is only by explicit call to - * QCBORDecode_ExitBoundedMode(). - */ - if(DecodeNesting_IsCurrentBounded(&(pMe->nesting))) { - /* Set the count to zero for definite-length arrays to indicate - * cursor is at end of bounded array/map */ - if(bMarkEnd) { - /* Used for definite and indefinite to signal end */ - DecodeNesting_ZeroMapOrArrayCount(&(pMe->nesting)); - - } - break; - } - - /* Finally, actually ascend one level. */ - DecodeNesting_Ascend(&(pMe->nesting)); - } - - uReturn = QCBOR_SUCCESS; - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS -Done: -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - - return uReturn; -} - - -/** - * @brief Ascending & Descending out of nesting levels (decode layer 2). - * - * @param[in] pMe Decoder context - * @param[out] pbBreak Set to true if extra break was consumed. - * @param[out] pDecodedItem The decoded item that work is done on. - - * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved - * features - * @retval QCBOR_ERR_HIT_END Unexpected end of input - * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered - * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. - * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. - * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode - * of half-precision disabled - * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all - * float decode is disabled. - * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of - * simple type in input. - * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array - * in input, but indefinite - * lengths disabled. - * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, - * but no string allocator. - * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. - * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in - * input, but indefinite-length - * strings are disabled. - * @retval QCBOR_ERR_TOO_MANY_TAGS Too many tag numbers on item. - * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array. - * @retval QCBOR_ERR_MAP_LABEL_TYPE Map label not string or integer. - * @retval QCBOR_ERR_NO_MORE_ITEMS Need more items for map or array. - * @retval QCBOR_ERR_BAD_BREAK Indefinite-length break in wrong - * place. - * @retval QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP Nesting deeper than QCBOR - * can handle. - * - * This handles the traversal descending into and asecnding out of - * maps, arrays and bstr-wrapped CBOR. It figures out the ends of - * definite- and indefinte-length maps and arrays by looking at the - * item count or finding CBOR breaks. It detects the ends of the - * top-level sequence and of bstr-wrapped CBOR by byte count. - */ -static QCBORError -QCBORDecode_Private_GetNextMapOrArray(QCBORDecodeContext *pMe, - bool *pbBreak, - QCBORItem *pDecodedItem, - uint32_t *puLabelEndOffset) -{ - QCBORError uReturn; - /* ==== First: figure out if at the end of a traversal ==== */ - - /* If out of bytes to consume, it is either the end of the - * top-level sequence of some bstr-wrapped CBOR that was entered. - * - * In the case of bstr-wrapped CBOR, the length of the - * UsefulInputBuf was set to that of the bstr-wrapped CBOR. When - * the bstr-wrapped CBOR is exited, the length is set back to the - * top-level's length or to the next highest bstr-wrapped CBOR. - */ - if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf)) == 0) { - uReturn = QCBOR_ERR_NO_MORE_ITEMS; - goto Done; - } - - /* Check to see if at the end of a bounded definite-length map or - * array. The check for a break ending indefinite-length array is - * later in QCBORDecode_NestLevelAscender(). - */ - if(DecodeNesting_IsAtEndOfBoundedLevel(&(pMe->nesting))) { - uReturn = QCBOR_ERR_NO_MORE_ITEMS; - goto Done; - } - - /* ==== Next: not at the end, so get another item ==== */ - uReturn = QCBORDecode_Private_GetNextMapEntry(pMe, pDecodedItem, puLabelEndOffset); - if(QCBORDecode_IsUnrecoverableError(uReturn)) { - /* Error is so bad that traversal is not possible. */ - goto Done; - } - - /* Record the nesting level for this data item before processing - * any of decrementing and descending. - */ - pDecodedItem->uNestingLevel = DecodeNesting_GetCurrentLevel(&(pMe->nesting)); - - - /* ==== Next: Process the item for descent, ascent, decrement... ==== */ - if(QCBORItem_IsMapOrArray(*pDecodedItem)) { - /* If the new item is a map or array, descend. - * - * Empty indefinite-length maps and arrays are descended into, - * but then ascended out of in the next chunk of code. - * - * Maps and arrays do count as items in the map/array that - * encloses them so a decrement needs to be done for them too, - * but that is done only when all the items in them have been - * processed, not when they are opened with the exception of an - * empty map or array. - */ - QCBORError uDescendErr; - uDescendErr = DecodeNesting_DescendMapOrArray(&(pMe->nesting), - pDecodedItem->uDataType, - pDecodedItem->val.uCount); - if(uDescendErr != QCBOR_SUCCESS) { - /* This error is probably a traversal error and it overrides - * the non-traversal error. - */ - uReturn = uDescendErr; - goto Done; - } - } - - if(!QCBORItem_IsMapOrArray(*pDecodedItem) || - QCBORItem_IsEmptyDefiniteLengthMapOrArray(*pDecodedItem) || - QCBORItem_IsIndefiniteLengthMapOrArray(*pDecodedItem)) { - /* The following cases are handled here: - * - A non-aggregate item like an integer or string - * - An empty definite-length map or array - * - An indefinite-length map or array that might be empty or might not. - * - * QCBORDecode_NestLevelAscender() does the work of decrementing the count - * for an definite-length map/array and break detection for an - * indefinite-0length map/array. If the end of the map/array was - * reached, then it ascends nesting levels, possibly all the way - * to the top level. - */ - QCBORError uAscendErr; - uAscendErr = QCBORDecode_Private_NestLevelAscender(pMe, true, pbBreak); - if(uAscendErr != QCBOR_SUCCESS) { - /* This error is probably a traversal error and it overrides - * the non-traversal error. - */ - uReturn = uAscendErr; - goto Done; - } - } - - /* ==== Last: tell the caller the nest level of the next item ==== */ - /* Tell the caller what level is next. This tells them what - * maps/arrays were closed out and makes it possible for them to - * reconstruct the tree with just the information returned in a - * QCBORItem. - */ - if(DecodeNesting_IsAtEndOfBoundedLevel(&(pMe->nesting))) { - /* At end of a bounded map/array; uNextNestLevel 0 to indicate this */ - pDecodedItem->uNextNestLevel = 0; - } else { - pDecodedItem->uNextNestLevel = DecodeNesting_GetCurrentLevel(&(pMe->nesting)); - } - -Done: - return uReturn; -} - - -/** - * @brief Decode tag content for select tags (decoding layer 1). - * - * @param[in] pMe The decode context. - * @param[out] pDecodedItem The decoded item. - * - * @return Decoding error code. - * - * CBOR tag numbers for the item were decoded in GetNext_TaggedItem(), - * but the whole tag was not decoded. Here, the whole tags (tag number - * and tag content) are decoded. This is a - * quick pass through for items that are not tags. - */ -QCBORError -QCBORDecode_Private_GetNextTagContent(QCBORDecodeContext *pMe, - QCBORItem *pDecodedItem) -{ - QCBORError uErr; - - uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem, NULL); - -#ifndef QCBOR_DISABLE_TAGS - uint64_t uTagNumber; - int nTagIndex; - const struct QCBORTagDecoderEntry *pTagDecoder; - - if(uErr != QCBOR_SUCCESS) { - goto Done; - } - - /* Loop over tag numbers in reverse, those closest to content first */ - for(nTagIndex = QCBOR_MAX_TAGS_PER_ITEM-1; nTagIndex >= 0; nTagIndex--) { - - if(pDecodedItem->auTagNumbers[nTagIndex] == CBOR_TAG_INVALID16) { - continue; /* Empty slot, skip to next */ - } - - /* See if there's a content decoder for it */ - uTagNumber = QCBORDecode_Private_UnMapTagNumber(pMe, pDecodedItem->auTagNumbers[nTagIndex]); - pTagDecoder = QCBORDecode_Private_LookUpTagDecoder(pMe->pTagDecoderTable, uTagNumber); - if(pTagDecoder == NULL) { - break; /* Successful exist -- a tag that we can't decode */ - } - - /* Call the content decoder */ - uErr = pTagDecoder->pfContentDecoder(pMe, pMe->pTagDecodersContext, pTagDecoder->uTagNumber, pDecodedItem); - if(uErr != QCBOR_SUCCESS) { - break; /* Error exit from the loop */ - } - - /* Remove tag number from list since its content was decoded */ - pDecodedItem->auTagNumbers[nTagIndex] = CBOR_TAG_INVALID16; - } - -Done: -#endif /* ! QCBOR_DISABLE_TAGS */ - - return uErr; -} - - -/** - * @brief Consume an entire map or array including its contents. - * - * @param[in] pMe The decoder context. - * @param[in] pItemToConsume The array/map whose contents are to be - * consumed. - * @param[out] puNextNestLevel The next nesting level after the item was - * fully consumed. - * - * This may be called when @c pItemToConsume is not an array or - * map. In that case, this is just a pass through for @c puNextNestLevel - * since there is nothing to do. - */ -static QCBORError -QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe, - const QCBORItem *pItemToConsume, - bool *pbBreak, - uint8_t *puNextNestLevel) -{ - QCBORError uReturn; - QCBORItem Item; - - /* If it is a map or array, this will tell if it is empty. */ - const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel); - - if(QCBORItem_IsMapOrArray(*pItemToConsume) && !bIsEmpty) { - /* There is only real work to do for non-empty maps and arrays */ - - /* This works for definite- and indefinite-length maps and - * arrays by using the nesting level - */ - do { - uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, pbBreak, &Item, NULL); - if(QCBORDecode_IsUnrecoverableError(uReturn) || - uReturn == QCBOR_ERR_NO_MORE_ITEMS) { - goto Done; - } - } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel); - - *puNextNestLevel = Item.uNextNestLevel; - - uReturn = QCBOR_SUCCESS; - - } else { - /* pItemToConsume is not a map or array. Just pass the nesting - * level through. */ - *puNextNestLevel = pItemToConsume->uNextNestLevel; - - uReturn = QCBOR_SUCCESS; - } - -Done: - return uReturn; -} - - -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE -/* - * This consumes the next item. It returns the starting position of - * the label and the length of the label. It also returns the nest - * level of the item consumed. - */ -static QCBORError -QCBORDecode_Private_GetLabelAndConsume(QCBORDecodeContext *pMe, - uint8_t *puNestLevel, - size_t *puLabelStart, - size_t *puLabelLen) -{ - QCBORError uErr; - QCBORItem Item; - uint8_t uLevel; - uint32_t uLabelOffset; - - /* Get the label and consume it should it be complex */ - *puLabelStart = UsefulInputBuf_Tell(&(pMe->InBuf)); - - uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, &Item, &uLabelOffset); - if(uErr != QCBOR_SUCCESS) { - goto Done; - } - *puLabelLen = uLabelOffset - *puLabelStart; - *puNestLevel = Item.uNestingLevel; - uErr = QCBORDecode_Private_ConsumeItem(pMe, &Item, NULL, &uLevel); - -Done: - return uErr; -} - - -/* Loop over items in a map until the end of the map looking for - * duplicates. This starts at the current position in the map, not at - * the beginning of the map. - * - * This saves and restores the traversal cursor and nest tracking so - * they are the same on exit as they were on entry. - */ -static QCBORError -QCBORDecode_Private_CheckDups(QCBORDecodeContext *pMe, - const uint8_t uNestLevel, - const size_t uCompareLabelStart, - const size_t uCompareLabelLen) -{ - QCBORError uErr; - size_t uLabelStart; - size_t uLabelLen; - uint8_t uLevel; - int nCompare; - - const QCBORDecodeNesting SaveNesting = pMe->nesting; - const UsefulInputBuf Save = pMe->InBuf; - - do { - uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uLevel, &uLabelStart, &uLabelLen); - if(uErr != QCBOR_SUCCESS) { - if(uErr == QCBOR_ERR_NO_MORE_ITEMS) { - uErr = QCBOR_SUCCESS; /* Successful end */ - } - break; - } - - if(uLevel != uNestLevel) { - break; /* Successful end of loop */ - } - - /* This check for dups works for labels that are preferred - * serialization and are not maps. If the labels are not in - * preferred serialization, then the check has to be more - * complicated and is type-specific because it uses the decoded - * value, not the encoded CBOR. It is further complicated for - * maps because the order of items in a map that is a label - * doesn't matter when checking that is is the duplicate of - * another map that is a label. QCBOR so far only turns on this - * dup checking as part of CDE checking which requires preferred - * serialization. See 5.6 in RFC 8949. - */ - nCompare = UsefulInputBuf_Compare(&(pMe->InBuf), - uCompareLabelStart, uCompareLabelLen, - uLabelStart, uLabelLen); - if(nCompare == 0) { - uErr = QCBOR_ERR_DUPLICATE_LABEL; - break; - } - } while (1); - - pMe->nesting = SaveNesting; - pMe->InBuf = Save; - - return uErr; -} - - -/* This does sort order and duplicate detection on a map. The and all - * it's members must be in preferred serialization so the comparisons - * work correctly. - */ -static QCBORError -QCBORDecode_Private_CheckMap(QCBORDecodeContext *pMe, const QCBORItem *pMapToCheck) -{ - QCBORError uErr; - uint8_t uNestLevel; - size_t offset2, offset1, length2, length1; - - const QCBORDecodeNesting SaveNesting = pMe->nesting; - const UsefulInputBuf Save = pMe->InBuf; - pMe->bAllowAllLabels = 1; - - /* This loop runs over all the items in the map once, comparing - * each adjacent pair for correct ordering. It also calls CheckDup - * on each one which also runs over the remaining items in the map - * checking for duplicates. So duplicate checking runs in n^2. - */ - - offset2 = SIZE_MAX; - length2 = SIZE_MAX; // To avoid uninitialized warning - while(1) { - uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uNestLevel, &offset1, &length1); - if(uErr != QCBOR_SUCCESS) { - if(uErr == QCBOR_ERR_NO_MORE_ITEMS) { - uErr = QCBOR_SUCCESS; /* Successful exit from loop */ - } - break; - } - - if(uNestLevel < pMapToCheck->uNextNestLevel) { - break; /* Successful exit from loop */ - } - - if(offset2 != SIZE_MAX) { - /* Check that the labels are ordered. Check is not done the - * first time through the loop when offset2 is unset. Since - * this does comparison of the items in encoded form they - * must be preferred serialization encoded. See RFC 8949 - * 4.2.1. - */ - if(UsefulInputBuf_Compare(&(pMe->InBuf), offset2, length2, offset1, length1) > 0) { - uErr = QCBOR_ERR_UNSORTED; - break; - } - } - - uErr = QCBORDecode_Private_CheckDups(pMe, pMapToCheck->uNextNestLevel, offset1, length1); - if(uErr != QCBOR_SUCCESS) { - break; - } - - offset2 = offset1; - length2 = length1; - } - - pMe->bAllowAllLabels = 0; - pMe->nesting = SaveNesting; - pMe->InBuf = Save; - - return uErr; -} -#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - -static QCBORError -QCBORDecode_Private_GetItemChecks(QCBORDecodeContext *pMe, - QCBORError uErr, - const size_t uOffset, - QCBORItem *pDecodedItem) -{ - (void)pMe; /* Avoid warning for next two ifdefs */ - (void)uOffset; - -#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE - if(uErr == QCBOR_SUCCESS && - pMe->uDecodeMode & QCBOR_DECODE_ONLY_SORTED_MAPS && - pDecodedItem->uDataType == QCBOR_TYPE_MAP) { - /* Traverse map checking sort order and for duplicates */ - uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem); - } -#endif /* ! QCBOR_DISABLE_CONFORMANCE */ - -#ifndef QCBOR_DISABLE_TAGS - if(uErr == QCBOR_SUCCESS && - !(pMe->uDecodeMode & QCBOR_DECODE_ALLOW_UNPROCESSED_TAG_NUMBERS) && - pDecodedItem->auTagNumbers[0] != CBOR_TAG_INVALID16) { - /* Not QCBOR v1; there are tag numbers -- check they were consumed */ - if(uOffset != pMe->uTagNumberCheckOffset || pMe->uTagNumberIndex != 255) { - uErr = QCBOR_ERR_UNPROCESSED_TAG_NUMBER; - } - } -#endif /* ! QCBOR_DISABLE_TAGS */ - - if(uErr != QCBOR_SUCCESS) { - pDecodedItem->uDataType = QCBOR_TYPE_NONE; - pDecodedItem->uLabelType = QCBOR_TYPE_NONE; - } - - return uErr; -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) -{ - QCBORError uErr; - size_t uOffset; - - uOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); - uErr = QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem); - uErr = QCBORDecode_Private_GetItemChecks(pMe, uErr, uOffset, pDecodedItem); - return uErr; -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_PeekNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) -{ - const QCBORDecodeNesting SaveNesting = pMe->nesting; - const UsefulInputBuf Save = pMe->InBuf; - - QCBORError uErr = QCBORDecode_GetNext(pMe, pDecodedItem); - - pMe->nesting = SaveNesting; - pMe->InBuf = Save; - - return uErr; -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_VPeekNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) -{ - if(pMe->uLastError != QCBOR_SUCCESS) { - pDecodedItem->uDataType = QCBOR_TYPE_NONE; - pDecodedItem->uLabelType = QCBOR_TYPE_NONE; - return; - } - - pMe->uLastError = (uint8_t)QCBORDecode_PeekNext(pMe, pDecodedItem); -} + if(bAllocate) { +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS + /* --- Put string in allocated memory --- */ + /* Note that this is not where allocation to coalesce + * indefinite-length strings is done. This is for when the + * caller has requested all strings be allocated. Disabling + * indefinite length strings also disables this allocate-all + * option. + */ -static void -QCBORDecode_Private_SaveTagNumbers(QCBORDecodeContext *pMe, const QCBORItem *pItem) -{ -#ifndef QCBOR_DISABLE_TAGS - memcpy(pMe->auLastTags, pItem->auTagNumbers, sizeof(pItem->auTagNumbers)); + if(pMe->StringAllocator.pfAllocator == NULL) { + uReturn = QCBOR_ERR_NO_STRING_ALLOCATOR; + goto Done; + } + UsefulBuf NewMem = StringAllocator_Allocate(&(pMe->StringAllocator), (size_t)uStrLen); + if(UsefulBuf_IsNULL(NewMem)) { + uReturn = QCBOR_ERR_STRING_ALLOCATE; + goto Done; + } + pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes); + pDecodedItem->uDataAlloc = 1; #else - (void)pMe; - (void)pItem; -#endif -} - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_VGetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) -{ - if(pMe->uLastError != QCBOR_SUCCESS) { - pDecodedItem->uDataType = QCBOR_TYPE_NONE; - pDecodedItem->uLabelType = QCBOR_TYPE_NONE; - return; - } - - pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, pDecodedItem); - QCBORDecode_Private_SaveTagNumbers(pMe, pDecodedItem); -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_PartialFinish(QCBORDecodeContext *pMe, size_t *puConsumed) -{ - if(puConsumed != NULL) { - *puConsumed = pMe->InBuf.cursor; - } - - QCBORError uReturn = pMe->uLastError; - - if(uReturn != QCBOR_SUCCESS) { - goto Done; - } - - /* Error out if all the maps/arrays are not closed out */ - if(!DecodeNesting_IsCurrentAtTop(&(pMe->nesting))) { - uReturn = QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED; - goto Done; - } - - /* Error out if not all the bytes are consumed */ - if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf))) { - uReturn = QCBOR_ERR_EXTRA_BYTES; + uReturn = QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED; +#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ + } else { + /* --- Normal case with no string allocator --- */ + pDecodedItem->val.string = Bytes; + } } Done: @@ -2792,1764 +591,1684 @@ QCBORDecode_PartialFinish(QCBORDecodeContext *pMe, size_t *puConsumed) } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_Finish(QCBORDecodeContext *pMe) -{ -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS - /* Call the destructor for the string allocator if there is one. - * Always called, even if there are errors; always have to clean up. - */ - StringAllocator_Destruct(&(pMe->StringAllocator)); -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ - - return QCBORDecode_PartialFinish(pMe, NULL); -} - - -#ifndef QCBOR_DISABLE_TAGS -/* - * Public function, see header qcbor/qcbor_decode.h file +/** + * @brief Decode array or map. + * + * @param[in] uConfigFlags Decoder mode. + * @param[in] nMajorType Whether it is a byte or text string. + * @param[in] uItemCount The length of the string. + * @param[in] nAdditionalInfo Whether it is an indefinite-length. + * @param[out] pDecodedItem The filled-in decoded item. + * + * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinites disabled. + * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array/map. + * + * Not much to do for arrays and maps. Just the type item count (but a + * little messy because of ifdefs for indefinite-lengths and + * map-as-array decoding). + * + * This also does the bulk of the work for @ref + * QCBOR_DECODE_MODE_MAP_AS_ARRAY, a special mode to handle + * arbitrarily complex map labels. This ifdefs out with + * QCBOR_DISABLE_NON_INTEGER_LABELS. */ -uint64_t -QCBORDecode_GetNthTagNumber(const QCBORDecodeContext *pMe, - const QCBORItem *pItem, - uint8_t uIndex) +static QCBORError +QCBOR_Private_DecodeArrayOrMap(const QCBORDecodeMode uConfigFlags, + const int nMajorType, + uint64_t uItemCount, + const int nAdditionalInfo, + QCBORItem *pDecodedItem) { - if(pItem->uDataType == QCBOR_TYPE_NONE) { - return CBOR_TAG_INVALID64; - } - if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) { - return CBOR_TAG_INVALID64; - } - - return QCBORDecode_Private_UnMapTagNumber(pMe, pItem->auTagNumbers[uIndex]); -} + QCBORError uReturn; + /* ------ Sort out the data type ------ */ + #if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY + #error QCBOR_TYPE_ARRAY value not lined up with major type + #endif -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -uint64_t -QCBORDecode_GetNthTagNumberOfLast(QCBORDecodeContext *pMe, - uint8_t uIndex) -{ - if(pMe->uLastError != QCBOR_SUCCESS) { - return CBOR_TAG_INVALID64; - } - if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) { - return CBOR_TAG_INVALID64; + #if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP + #error QCBOR_TYPE_MAP value not lined up with major type + #endif + pDecodedItem->uDataType = (uint8_t)nMajorType; +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + if((uConfigFlags & QCBOR_DECODE_MODE_MAP_AS_ARRAY) && nMajorType == QCBOR_TYPE_MAP) { + pDecodedItem->uDataType = QCBOR_TYPE_MAP_AS_ARRAY; } +#else + (void)uConfigFlags; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - return QCBORDecode_Private_UnMapTagNumber(pMe, pMe->auLastTags[uIndex]); -} + uReturn = QCBOR_SUCCESS; + if(nAdditionalInfo == LEN_IS_INDEFINITE) { + /* ------ Indefinite-length array/map ----- */ +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS + pDecodedItem->val.uCount = QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH; +#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ + uReturn = QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED; +#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ + } else { + /* ----- Definite-length array/map ----- */ + if(uItemCount > (nMajorType == QCBOR_TYPE_MAP ? QCBOR_MAX_ITEMS_IN_MAP : QCBOR_MAX_ITEMS_IN_ARRAY)) { + uReturn = QCBOR_ERR_ARRAY_DECODE_TOO_LONG; -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -static uint64_t -QCBORDecode_Private_GetNthTagNumberReverse(const QCBORDecodeContext *pMe, - const uint16_t puTagNumbers[], - const uint32_t uIndex) -{ - uint32_t uArrayIndex; + } else { +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + if((uConfigFlags & QCBOR_DECODE_MODE_MAP_AS_ARRAY) && nMajorType == QCBOR_TYPE_MAP) { + /* ------ Map as array ------ */ + uItemCount *= 2; + } +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - /* Find number of tag numbers */ - for(uArrayIndex = QCBOR_MAX_TAGS_PER_ITEM-1; uArrayIndex > 0; uArrayIndex--) { - if(puTagNumbers[uArrayIndex] != CBOR_TAG_INVALID16) { - break; + /* cast OK because of check above */ + pDecodedItem->val.uCount = (uint16_t)uItemCount; } } - if(uIndex > uArrayIndex) { - return CBOR_TAG_INVALID64; - } - return QCBORDecode_Private_UnMapTagNumber(pMe, puTagNumbers[uArrayIndex - uIndex]); + return uReturn; } -/* - * Public function, see header qcbor/qcbor_decode.h file +/** + * @brief Decode a tag number. + * + * @param[in] uTagNumber The length of the string. + * @param[in] nAdditionalInfo So this can be error-checked. + * @param[out] pDecodedItem The filled-in decoded item. + * + * @retval QCBOR_ERR_BAD_INT nAdditionalInfo is LEN_IS_INDEFINITE. + * @retval QCBOR_ERR_TAGS_DISABLED QCBOR_DISABLE_TAGS is defined. + * + * Not much to do for tags, but fill in pDecodedItem and check for + * error in nAdditionalInfo. */ -uint64_t -QCBORDecode_GetNthTag(QCBORDecodeContext *pMe, - const QCBORItem *pItem, - const uint32_t uIndex) +static QCBORError +QCBOR_Private_DecodeTagNumber(const uint64_t uTagNumber, + const int nAdditionalInfo, + QCBORItem *pDecodedItem) { - if(pItem->uDataType == QCBOR_TYPE_NONE) { - return CBOR_TAG_INVALID64; +#ifndef QCBOR_DISABLE_TAGS + if(nAdditionalInfo == LEN_IS_INDEFINITE) { + return QCBOR_ERR_BAD_INT; + } else { + pDecodedItem->val.uTagNumber = uTagNumber; + pDecodedItem->uDataType = QCBOR_TYPE_TAG_NUMBER; + return QCBOR_SUCCESS; } - - return QCBORDecode_Private_GetNthTagNumberReverse(pMe, pItem->auTagNumbers, uIndex); +#else /* QCBOR_DISABLE_TAGS */ + (void)nAdditionalInfo; + (void)uTagNumber; + (void)pDecodedItem; + return QCBOR_ERR_TAGS_DISABLED; +#endif /* QCBOR_DISABLE_TAGS */ } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -uint64_t -QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pMe, - uint32_t uIndex) +#ifndef USEFULBUF_DISABLE_ALL_FLOAT + +#if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT) + +static QCBORError +QCBORDecode_Private_HalfConformance(const double d, const QCBORDecodeMode uConfigFlags) { - if(pMe->uLastError != QCBOR_SUCCESS) { - return CBOR_TAG_INVALID64; - } - if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) { - return CBOR_TAG_INVALID64; + struct IEEE754_ToInt ToInt; + + /* Only need to check for conversion to integer because + * half-precision is always preferred serialization. Don't + * need special checker for half-precision because whole + * numbers always convert perfectly from half to double. + * + * This catches half-precision with NaN payload too. + * + * The only thing allowed here is a double/half-precision that + * can't be converted to anything but a double. + */ + if(uConfigFlags & QCBOR_DECODE_ONLY_REDUCED_FLOATS) { + ToInt = IEEE754_DoubleToInt(d); + if(ToInt.type != QCBOR_TYPE_DOUBLE) { + return QCBOR_ERR_DCBOR_CONFORMANCE; + } } - return QCBORDecode_Private_GetNthTagNumberReverse(pMe, pMe->auLastTags, uIndex); + return QCBOR_SUCCESS; } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_GetNextTagNumber(QCBORDecodeContext *pMe, uint64_t *puTagNumber) +static QCBORError +QCBORDecode_Private_SingleConformance(const float f, const QCBORDecodeMode uconfigFlags) { - QCBORItem Item; - size_t uOffset; - QCBORError uErr; - - const QCBORDecodeNesting SaveNesting = pMe->nesting; - const UsefulInputBuf Save = pMe->InBuf; + struct IEEE754_ToInt ToInt; + IEEE754_union ToSmaller; - uOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); - if(uOffset == pMe->uTagNumberCheckOffset) { - pMe->uTagNumberIndex++; - } else { - pMe->uTagNumberIndex = 0; - } + if(uconfigFlags & QCBOR_DECODE_ONLY_REDUCED_FLOATS) { + /* See if it could have been encoded as an integer */ + ToInt = IEEE754_SingleToInt(f); + if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) { + return QCBOR_ERR_DCBOR_CONFORMANCE; + } - *puTagNumber = CBOR_TAG_INVALID64; - uErr = QCBORDecode_Private_GetNextTagContent(pMe, &Item); - if(uErr) { - return uErr; + /* Make sure there is no NaN payload */ + if(IEEE754_SingleHasNaNPayload(f)) { + return QCBOR_ERR_DCBOR_CONFORMANCE; + } } - *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &Item, pMe->uTagNumberIndex); - if(*puTagNumber == CBOR_TAG_INVALID64 || - QCBORDecode_GetNthTagNumber(pMe, &Item, pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) { - pMe->uTagNumberIndex = QCBOR_ALL_TAGS_PROCESSED; + /* See if it could have been encoded shorter */ + if(uconfigFlags & QCBOR_DECODE_ONLY_PREFERRED_NUMBERS) { + ToSmaller = IEEE754_SingleToHalf(f, true); + if(ToSmaller.uSize != sizeof(float)) { + return QCBOR_ERR_PREFERRED_CONFORMANCE; + } } - pMe->uTagNumberCheckOffset = uOffset; - - pMe->nesting = SaveNesting; - pMe->InBuf = Save; return QCBOR_SUCCESS; } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_VGetNextTagNumber(QCBORDecodeContext *pMe, uint64_t *puTagNumber) +static QCBORError +QCBORDecode_Private_DoubleConformance(const double d, QCBORDecodeMode uConfigFlags) { - pMe->uLastError = (uint8_t)QCBORDecode_GetNextTagNumber(pMe, puTagNumber); -} - -#endif /* ! QCBOR_DISABLE_TAGS */ - -#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS - -/* =========================================================================== - MemPool -- BUILT-IN SIMPLE STRING ALLOCATOR - - This implements a simple sting allocator for indefinite-length - strings that can be enabled by calling QCBORDecode_SetMemPool(). It - implements the function type QCBORStringAllocate and allows easy - use of it. - - This particular allocator is built-in for convenience. The caller - can implement their own. All of this following code will get - dead-stripped if QCBORDecode_SetMemPool() is not called. - - This is a very primitive memory allocator. It does not track - individual allocations, only a high-water mark. A free or - reallocation must be of the last chunk allocated. + struct IEEE754_ToInt ToInt; + IEEE754_union ToSmaller; - The size of the pool and offset to free memory are packed into the - first 8 bytes of the memory pool so we don't have to keep them in - the decode context. Since the address of the pool may not be - aligned, they have to be packed and unpacked as if they were - serialized data of the wire or such. + if(uConfigFlags & QCBOR_DECODE_ONLY_REDUCED_FLOATS) { + /* See if it could have been encoded as an integer */ + ToInt = IEEE754_DoubleToInt(d); + if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) { + return QCBOR_ERR_DCBOR_CONFORMANCE; + } + /* Make sure there is no NaN payload */ + if(IEEE754_DoubleHasNaNPayload(d)) { + return QCBOR_ERR_DCBOR_CONFORMANCE; + } + } - The sizes packed in are uint32_t to be the same on all CPU types - and simplify the code. - ========================================================================== */ + /* See if it could have been encoded shorter */ + if(uConfigFlags & QCBOR_DECODE_ONLY_PREFERRED_NUMBERS) { + ToSmaller = IEEE754_DoubleToSmaller(d, true, true); + if(ToSmaller.uSize != sizeof(double)) { + return QCBOR_ERR_PREFERRED_CONFORMANCE; + } + } + return QCBOR_SUCCESS; +} +#else /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */ -static int -MemPool_Unpack(const void *pMem, uint32_t *puPoolSize, uint32_t *puFreeOffset) +static QCBORError +QCBORDecode_Private_SingleConformance(const float f, const QCBORDecodeMode uConfigFlags) { - // Use of UsefulInputBuf is overkill, but it is convenient. - UsefulInputBuf UIB; - - // Just assume the size here. It was checked during SetUp so - // the assumption is safe. - UsefulInputBuf_Init(&UIB, (UsefulBufC){pMem,QCBOR_DECODE_MIN_MEM_POOL_SIZE}); - *puPoolSize = UsefulInputBuf_GetUint32(&UIB); - *puFreeOffset = UsefulInputBuf_GetUint32(&UIB); - return UsefulInputBuf_GetError(&UIB); + (void)f; + if(uConfigFlags & (QCBOR_DECODE_ONLY_REDUCED_FLOATS | QCBOR_DECODE_ONLY_PREFERRED_NUMBERS)) { + return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE; + } else { + return QCBOR_SUCCESS; + } } - -static int -MemPool_Pack(UsefulBuf Pool, uint32_t uFreeOffset) +static QCBORError +QCBORDecode_Private_DoubleConformance(const double d, const QCBORDecodeMode uConfigFlags) { - // Use of UsefulOutBuf is overkill, but convenient. The - // length check performed here is useful. - UsefulOutBuf UOB; - - UsefulOutBuf_Init(&UOB, Pool); - UsefulOutBuf_AppendUint32(&UOB, (uint32_t)Pool.len); // size of pool - UsefulOutBuf_AppendUint32(&UOB, uFreeOffset); // first free position - return UsefulOutBuf_GetError(&UOB); + (void)d; + if(uConfigFlags & (QCBOR_DECODE_ONLY_REDUCED_FLOATS | QCBOR_DECODE_ONLY_PREFERRED_NUMBERS)) { + return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE; + } else { + return QCBOR_SUCCESS; + } } +#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */ /* - Internal function for an allocation, reallocation free and destuct. - - Having only one function rather than one each per mode saves space in - QCBORDecodeContext. - - Code Reviewers: THIS FUNCTION DOES POINTER MATH + * Decode a float */ -static UsefulBuf -MemPool_Function(void *pPool, void *pMem, size_t uNewSize) +static QCBORError +QCBOR_Private_DecodeFloat(const QCBORDecodeMode uConfigFlags, + const int nAdditionalInfo, + const uint64_t uArgument, + QCBORItem *pDecodedItem) { - UsefulBuf ReturnValue = NULLUsefulBuf; + QCBORError uReturn = QCBOR_SUCCESS; + float single; - uint32_t uPoolSize; - uint32_t uFreeOffset; + (void)single; /* Avoid unused error from various #ifndefs */ - if(uNewSize > UINT32_MAX) { - // This allocator is only good up to 4GB. This check should - // optimize out if sizeof(size_t) == sizeof(uint32_t) - goto Done; - } - const uint32_t uNewSize32 = (uint32_t)uNewSize; + switch(nAdditionalInfo) { + case HALF_PREC_FLOAT: /* 25 */ +#ifndef QCBOR_DISABLE_PREFERRED_FLOAT + /* Half-precision is returned as a double. The cast to + * uint16_t is safe because the encoded value was 16 bits. It + * was widened to 64 bits to be passed in here. + */ + pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument); + pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE; - if(MemPool_Unpack(pPool, &uPoolSize, &uFreeOffset)) { - goto Done; - } + uReturn = QCBORDecode_Private_HalfConformance(pDecodedItem->val.dfnum, uConfigFlags); + if(uReturn != QCBOR_SUCCESS) { + break; + } +#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */ + uReturn = FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS); + break; - if(uNewSize) { - if(pMem) { - // REALLOCATION MODE - // Calculate pointer to the end of the memory pool. It is - // assumed that pPool + uPoolSize won't wrap around by - // assuming the caller won't pass a pool buffer in that is - // not in legitimate memory space. - const void *pPoolEnd = (uint8_t *)pPool + uPoolSize; + case SINGLE_PREC_FLOAT: /* 26 */ + /* Single precision is normally returned as a double since + * double is widely supported, there is no loss of precision, + * it makes it easy for the caller in most cases and it can + * be converted back to single with no loss of precision + * + * The cast to uint32_t is safe because the encoded value was + * 32 bits. It was widened to 64 bits to be passed in here. + */ + single = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument); + uReturn = QCBORDecode_Private_SingleConformance(single, uConfigFlags); + if(uReturn != QCBOR_SUCCESS) { + break; + } - // Check that the pointer for reallocation is in the range of the - // pool. This also makes sure that pointer math further down - // doesn't wrap under or over. - if(pMem >= pPool && pMem < pPoolEnd) { - // Offset to start of chunk for reallocation. This won't - // wrap under because of check that pMem >= pPool. Cast - // is safe because the pool is always less than UINT32_MAX - // because of check in QCBORDecode_SetMemPool(). - const uint32_t uMemOffset = (uint32_t)((uint8_t *)pMem - (uint8_t *)pPool); +#ifndef QCBOR_DISABLE_FLOAT_HW_USE + /* In the normal case, use HW to convert float to + * double. */ + pDecodedItem->val.dfnum = (double)single; + pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE; +#else /* QCBOR_DISABLE_FLOAT_HW_USE */ + /* Use of float HW is disabled, return as a float. */ + pDecodedItem->val.fnum = single; + pDecodedItem->uDataType = QCBOR_TYPE_FLOAT; + + /* IEEE754_FloatToDouble() could be used here to return as + * a double, but it adds object code and most likely + * anyone disabling FLOAT HW use doesn't care about floats + * and wants to save object code. + */ +#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */ + uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS); + break; - // Check to see if the allocation will fit. uPoolSize - - // uMemOffset will not wrap under because of check that - // pMem is in the range of the uPoolSize by check above. - if(uNewSize <= uPoolSize - uMemOffset) { - ReturnValue.ptr = pMem; - ReturnValue.len = uNewSize; + case DOUBLE_PREC_FLOAT: /* 27 */ + pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument); + pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE; - // Addition won't wrap around over because uNewSize was - // checked to be sure it is less than the pool size. - uFreeOffset = uMemOffset + uNewSize32; - } - } - } else { - // ALLOCATION MODE - // uPoolSize - uFreeOffset will not underflow because this - // pool implementation makes sure uFreeOffset is always - // smaller than uPoolSize through this check here and - // reallocation case. - if(uNewSize <= uPoolSize - uFreeOffset) { - ReturnValue.len = uNewSize; - ReturnValue.ptr = (uint8_t *)pPool + uFreeOffset; - uFreeOffset += (uint32_t)uNewSize; + uReturn = QCBORDecode_Private_DoubleConformance(pDecodedItem->val.dfnum, uConfigFlags); + if(uReturn != QCBOR_SUCCESS) { + break; } - } - } else { - if(pMem) { - // FREE MODE - // Cast is safe because of limit on pool size in - // QCBORDecode_SetMemPool() - uFreeOffset = (uint32_t)((uint8_t *)pMem - (uint8_t *)pPool); - } else { - // DESTRUCT MODE - // Nothing to do for this allocator - } + uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS); + break; } - UsefulBuf Pool = {pPool, uPoolSize}; - MemPool_Pack(Pool, uFreeOffset); - -Done: - return ReturnValue; + return uReturn; } +#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */ -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_SetMemPool(QCBORDecodeContext *pMe, - UsefulBuf Pool, - bool bAllStrings) -{ - // The pool size and free mem offset are packed into the beginning - // of the pool memory. This compile time check makes sure the - // constant in the header is correct. This check should optimize - // down to nothing. -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4127) // conditional expression is constant -#endif - if(QCBOR_DECODE_MIN_MEM_POOL_SIZE < 2 * sizeof(uint32_t)) { - return QCBOR_ERR_MEM_POOL_SIZE; - } -#ifdef _MSC_VER -#pragma warning(pop) + +/* Make sure #define value line up as DecodeSimple counts on this. */ +#if QCBOR_TYPE_FALSE != CBOR_SIMPLEV_FALSE +#error QCBOR_TYPE_FALSE macro value wrong #endif - // The pool size and free offset packed in to the beginning of pool - // memory are only 32-bits. This check will optimize out on 32-bit - // machines. - if(Pool.len > UINT32_MAX) { - return QCBOR_ERR_MEM_POOL_SIZE; - } +#if QCBOR_TYPE_TRUE != CBOR_SIMPLEV_TRUE +#error QCBOR_TYPE_TRUE macro value wrong +#endif - // This checks that the pool buffer given is big enough. - if(MemPool_Pack(Pool, QCBOR_DECODE_MIN_MEM_POOL_SIZE)) { - return QCBOR_ERR_MEM_POOL_SIZE; - } +#if QCBOR_TYPE_NULL != CBOR_SIMPLEV_NULL +#error QCBOR_TYPE_NULL macro value wrong +#endif - QCBORDecode_SetUpAllocator(pMe, MemPool_Function, Pool.ptr, bAllStrings); +#if QCBOR_TYPE_UNDEF != CBOR_SIMPLEV_UNDEF +#error QCBOR_TYPE_UNDEF macro value wrong +#endif - return QCBOR_SUCCESS; -} -#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ +#if QCBOR_TYPE_BREAK != CBOR_SIMPLE_BREAK +#error QCBOR_TYPE_BREAK macro value wrong +#endif +#if QCBOR_TYPE_DOUBLE != DOUBLE_PREC_FLOAT +#error QCBOR_TYPE_DOUBLE macro value wrong +#endif +#if QCBOR_TYPE_FLOAT != SINGLE_PREC_FLOAT +#error QCBOR_TYPE_FLOAT macro value wrong +#endif -/* - * Public function, see header qcbor/qcbor_decode.h file +/** + * @brief Decode major type 7 -- true, false, floating-point, break... + * + * @param[in] nAdditionalInfo The lower five bits from the initial byte. + * @param[in] uArgument The argument from the head. + * @param[out] pDecodedItem The filled in decoded item. + * + * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode + * of half-precision disabled + * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all float + * decode is disabled. + * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of simple + * type in input. */ -void -QCBORDecode_VGetNextConsume(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) +static QCBORError +QCBOR_Private_DecodeType7(const QCBORDecodeMode uConfigFlags, + const int nAdditionalInfo, + const uint64_t uArgument, + QCBORItem *pDecodedItem) { - QCBORDecode_VGetNext(pMe, pDecodedItem); + QCBORError uReturn = QCBOR_SUCCESS; - if(pMe->uLastError == QCBOR_SUCCESS) { - pMe->uLastError = (uint8_t)QCBORDecode_Private_ConsumeItem(pMe, pDecodedItem, NULL, - &pDecodedItem->uNextNestLevel); - } -} + /* uAdditionalInfo is 5 bits from the initial byte. Compile time + * checks above make sure uAdditionalInfo values line up with + * uDataType values. DecodeHead() never returns an AdditionalInfo + * > 0x1f so cast is safe. + */ + pDecodedItem->uDataType = (uint8_t)nAdditionalInfo; + switch(nAdditionalInfo) { + /* No check for ADDINFO_RESERVED1 - ADDINFO_RESERVED3 as they + * are caught before this is called. + */ -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_EndCheck(QCBORDecodeContext *pMe) -{ - size_t uCursorOffset; - QCBORError uErr; + case HALF_PREC_FLOAT: /* 25 */ + case SINGLE_PREC_FLOAT: /* 26 */ + case DOUBLE_PREC_FLOAT: /* 27 */ +#ifndef USEFULBUF_DISABLE_ALL_FLOAT + uReturn = QCBOR_Private_DecodeFloat(uConfigFlags, nAdditionalInfo, uArgument, pDecodedItem); +#else + uReturn = QCBOR_ERR_ALL_FLOAT_DISABLED; +#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */ + break; - uErr = QCBORDecode_GetError(pMe); - if(uErr != QCBOR_SUCCESS) { - return uErr; - } + case CBOR_SIMPLEV_FALSE: /* 20 */ + case CBOR_SIMPLEV_TRUE: /* 21 */ + case CBOR_SIMPLEV_NULL: /* 22 */ + case CBOR_SIMPLEV_UNDEF: /* 23 */ + case CBOR_SIMPLE_BREAK: /* 31 */ +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + if((uConfigFlags & QCBOR_DECODE_DISALLOW_DCBOR_SIMPLES) && + nAdditionalInfo == CBOR_SIMPLEV_UNDEF) { + uReturn = QCBOR_ERR_DCBOR_CONFORMANCE; + goto Done; + } +#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ + break; /* nothing to do */ - uCursorOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); + case CBOR_SIMPLEV_ONEBYTE: /* 24 */ + if(uArgument <= CBOR_SIMPLE_BREAK) { + /* This takes out f8 00 ... f8 1f which should be encoded + * as e0 … f7 -- preferred serialization check for simple values. + */ + uReturn = QCBOR_ERR_BAD_TYPE_7; + goto Done; + } + /* FALLTHROUGH */ - if(uCursorOffset == UsefulInputBuf_GetBufferLength(&(pMe->InBuf))) { - return QCBOR_ERR_NO_MORE_ITEMS; + default: /* 0-19 */ +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + if((uConfigFlags & QCBOR_DECODE_DISALLOW_DCBOR_SIMPLES) && + (uArgument < CBOR_SIMPLEV_FALSE || uArgument > CBOR_SIMPLEV_NULL)) { + uReturn = QCBOR_ERR_DCBOR_CONFORMANCE; + goto Done; + } +#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */ + + pDecodedItem->uDataType = QCBOR_TYPE_UKNOWN_SIMPLE; + /* QCBOR_Private_DecodeHead() will make uArgument equal to + * nAdditionalInfo when nAdditionalInfo is < 24. This cast is + * safe because the 2, 4 and 8 byte lengths of uNumber are in + * the double/float cases above + */ + pDecodedItem->val.uSimple = (uint8_t)uArgument; + break; } - return QCBOR_SUCCESS; +Done: + return uReturn; } /** - * @brief Rewind cursor to start as if map or array were just entered. + * @brief Decode a single primitive data item (decode layer 6). * - * @param[in] pMe The decoding context + * @param[in] pMe Decoder context. + * @param[in] bAllocateStrings If true, use allocator for strings. + * @param[out] pDecodedItem The filled-in decoded item. * - * This affects the nesting tracking and the UsefulInputBuf. - */ -static void -QCBORDecode_Private_RewindMapOrArray(QCBORDecodeContext *pMe) -{ - /* Reset nesting tracking to the deepest bounded level */ - DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting)); - - DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting)); - - /* Reposition traversal cursor to the start of the map/array */ - UsefulInputBuf_Seek(&(pMe->InBuf), - DecodeNesting_GetMapOrArrayStart(&(pMe->nesting))); -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file + * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved + * features + * @retval QCBOR_ERR_HIT_END Unexpected end of input + * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered + * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. + * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. + * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Allocation requested, but no allocator + * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode + * of half-precision disabled + * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all + * float decode is disabled. + * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of + * simple type in input. + * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array + * in input, but indefinite + * lengths disabled. + * @retval QCBOR_ERR_BAD_INT nAdditionalInfo indicated indefinte. + * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array/map. + * @retval QCBOR_ERR_TAGS_DISABLED QCBOR_DISABLE_TAGS is defined. + * + * This decodes the most primitive/atomic data item. It does no + * combining of data items. */ -void -QCBORDecode_Rewind(QCBORDecodeContext *pMe) +static QCBORError +QCBOR_Private_DecodeAtomicDataItem(QCBORDecodeContext *pMe, + const bool bAllocateStrings, + QCBORItem *pDecodedItem) { - if(pMe->nesting.pCurrentBounded != NULL) { - /* In a bounded map, array or bstr-wrapped CBOR */ - - if(DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_BYTE_STRING)) { - /* In bstr-wrapped CBOR. */ - - /* Reposition traversal cursor to start of wrapping byte string */ - UsefulInputBuf_Seek(&(pMe->InBuf), - pMe->nesting.pCurrentBounded->u.bs.uBstrStartOffset); - DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting)); + QCBORError uReturn; + int nMajorType = 0; + uint64_t uArgument = 0; + int nAdditionalInfo = 0; - } else { - /* In a map or array */ - QCBORDecode_Private_RewindMapOrArray(pMe); - } + const QCBORDecodeMode uDecodeMode = pMe->uDecodeMode; - } else { - /* Not in anything bounded */ + memset(pDecodedItem, 0, sizeof(QCBORItem)); - /* Reposition traversal cursor to the start of input CBOR */ - UsefulInputBuf_Seek(&(pMe->InBuf), 0ULL); + /* Decode the "head" that every CBOR item has into the major type, + * argument and the additional info. + */ + uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf), +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + // TODO: make this prettier; will optimizer take out stuff without ifdef? + uDecodeMode, +#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */ + &nMajorType, + &uArgument, + &nAdditionalInfo); - /* Reset nesting tracking to beginning of input. */ - DecodeNesting_Init(&(pMe->nesting)); + if(uReturn != QCBOR_SUCCESS) { + return uReturn; } - pMe->uLastError = QCBOR_SUCCESS; -} - + /* All the functions below get inlined by the optimizer. This code + * is easier to read with them all being similar functions, even if + * some functions don't do much. + */ + switch (nMajorType) { + case CBOR_MAJOR_TYPE_POSITIVE_INT: /* Major type 0 */ + case CBOR_MAJOR_TYPE_NEGATIVE_INT: /* Major type 1 */ + return QCBOR_Private_DecodeInteger(nMajorType, uArgument, nAdditionalInfo, pDecodedItem); + break; + case CBOR_MAJOR_TYPE_BYTE_STRING: /* Major type 2 */ + case CBOR_MAJOR_TYPE_TEXT_STRING: /* Major type 3 */ + return QCBOR_Private_DecodeString(pMe, bAllocateStrings, nMajorType, uArgument, nAdditionalInfo, pDecodedItem); + break; + case CBOR_MAJOR_TYPE_ARRAY: /* Major type 4 */ + case CBOR_MAJOR_TYPE_MAP: /* Major type 5 */ + return QCBOR_Private_DecodeArrayOrMap(uDecodeMode, nMajorType, uArgument, nAdditionalInfo, pDecodedItem); + break; + case CBOR_MAJOR_TYPE_TAG: /* Major type 6, tag numbers */ + return QCBOR_Private_DecodeTagNumber(uArgument, nAdditionalInfo, pDecodedItem); + break; -typedef struct { - void *pCBContext; - QCBORItemCallback pfCallback; -} MapSearchCallBack; + case CBOR_MAJOR_TYPE_SIMPLE: + /* Major type 7: float, double, true, false, null... */ + return QCBOR_Private_DecodeType7(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem); + break; -typedef struct { - size_t uStartOffset; - uint16_t uItemCount; -} MapSearchInfo; + default: + /* Never happens because DecodeHead() should never return > 7 */ + return QCBOR_ERR_UNSUPPORTED; + break; + } +} /** - * @brief Search a map for a set of items. - * - * @param[in] pMe The decode context to search. - * @param[in,out] pItemArray The items to search for and the items found. - * @param[out] pInfo Several bits of meta-info returned by search. - * @param[in] pCallBack Callback object or @c NULL. - * - * @retval QCBOR_ERR_NOT_ENTERED Trying to search without entering a map. - * - * @retval QCBOR_ERR_DUPLICATE_LABEL Duplicate items (items with the same label) - * were found for one of the labels being - * search for. This duplicate detection is - * only performed for items in pItemArray, - * not every item in the map. - * - * @retval QCBOR_ERR_UNEXPECTED_TYPE A label was matched, but the type was - * wrong for the matchd label. + * @brief Process indefinite-length strings (decode layer 5). * - * @retval Also errors returned by QCBORDecode_GetNext(). + * @param[in] pMe Decoder context + * @param[out] pDecodedItem The decoded item that work is done on. * - * On input, @c pItemArray contains a list of labels and data types of - * items to be found. + * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved + * features + * @retval QCBOR_ERR_HIT_END Unexpected end of input + * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered + * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. + * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. + * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode + * of half-precision disabled + * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all + * float decode is disabled. + * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of + * simple type in input. + * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array + * in input, but indefinite + * lengths disabled. + * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, + * but no string allocator. + * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. + * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in + * input, but indefinite-length + * strings are disabled. * - * On output, the fully retrieved items are filled in with values and - * such. The label was matched, so it never changes. + * If @c pDecodedItem is not an indefinite-length string, this does nothing. * - * If an item was not found, its data type is set to @ref QCBOR_TYPE_NONE. + * If it is, this loops getting the subsequent chunk data items that + * make up the string. The string allocator is used to make a + * contiguous buffer for the chunks. When this completes @c + * pDecodedItem contains the put-together string. * - * This also finds the ends of maps and arrays when they are exited. + * Code Reviewers: THIS FUNCTION DOES A LITTLE POINTER MATH */ static QCBORError -QCBORDecode_Private_MapSearch(QCBORDecodeContext *pMe, - QCBORItem *pItemArray, - MapSearchInfo *pInfo, - MapSearchCallBack *pCallBack) +QCBORDecode_Private_GetNextFullString(QCBORDecodeContext *pMe, + QCBORItem *pDecodedItem) { + /* Aproximate stack usage + * 64-bit 32-bit + * local vars 32 16 + * 2 UsefulBufs 32 16 + * QCBORItem 56 52 + * TOTAL 120 74 + */ QCBORError uReturn; - uint64_t uFoundItemBitMap = 0; - if(pMe->uLastError != QCBOR_SUCCESS) { - uReturn = pMe->uLastError; - goto Done2; - } + /* A note about string allocation -- Memory for strings is + * allocated either because 1) indefinte-length string chunks are + * being coalecsed or 2) caller has requested all strings be + * allocated. The first case is handed below here. The second case + * is handled in DecodeString if the bAllocate is true. That + * boolean originates here with pMe->bStringAllocateAll immediately + * below. That is, QCBOR_Private_DecodeAtomicDataItem() is called + * in two different contexts here 1) main-line processing which is + * where definite-length strings need to be allocated if + * bStringAllocateAll is true and 2) processing chunks of + * indefinite-lengths strings in in which case there must be no + * allocation. + */ - if(!DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_MAP) && - pItemArray->uLabelType != QCBOR_TYPE_NONE) { - /* QCBOR_TYPE_NONE as first item indicates just looking - for the end of an array, so don't give error. */ - uReturn = QCBOR_ERR_MAP_NOT_ENTERED; - goto Done2; - } - if(DecodeNesting_IsBoundedEmpty(&(pMe->nesting))) { - // It is an empty bounded array or map - if(pItemArray->uLabelType == QCBOR_TYPE_NONE) { - // Just trying to find the end of the map or array - pMe->uMapEndOffsetCache = DecodeNesting_GetMapOrArrayStart(&(pMe->nesting)); - uReturn = QCBOR_SUCCESS; - } else { - // Nothing is ever found in an empty array or map. All items - // are marked as not found below. - uReturn = QCBOR_SUCCESS; - } - goto Done2; + uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, pMe->bStringAllocateAll, pDecodedItem); + if(uReturn != QCBOR_SUCCESS) { + goto Done; } - QCBORDecodeNesting SaveNesting; - size_t uSavePos = UsefulInputBuf_Tell(&(pMe->InBuf)); - DecodeNesting_PrepareForMapSearch(&(pMe->nesting), &SaveNesting); - - /* Reposition to search from the start of the map / array */ - QCBORDecode_Private_RewindMapOrArray(pMe); - - /* - Loop over all the items in the map or array. Each item - could be a map or array, but label matching is only at - the main level. This handles definite- and indefinite- - length maps and arrays. The only reason this is ever - called on arrays is to find their end position. - - This will always run over all items in order to do - duplicate detection. - - This will exit with failure if it encounters an - unrecoverable error, but continue on for recoverable - errors. - - If a recoverable error occurs on a matched item, then - that error code is returned. + /* This is where out-of-place break is detected for the whole + * decoding stack. Break is an error for everything that calls + * QCBORDecode_Private_GetNextFullString(), so the check is + * centralized here. */ - const uint8_t uMapNestLevel = DecodeNesting_GetBoundedModeLevel(&(pMe->nesting)); - if(pInfo) { - pInfo->uItemCount = 0; + if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) { + uReturn = QCBOR_ERR_BAD_BREAK; + goto Done; } - uint8_t uNextNestLevel; - do { - /* Remember offset of the item because sometimes it has to be returned */ - const size_t uOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); - - /* Get the item */ - QCBORItem Item; - /* QCBORDecode_Private_GetNextTagContent() rather than GetNext() - * because a label match is performed on recoverable errors to - * be able to return the the error code for the found item. */ - QCBORError uResult = QCBORDecode_Private_GetNextTagContent(pMe, &Item); - if(QCBORDecode_IsUnrecoverableError(uResult)) { - /* The map/array can't be decoded when unrecoverable errors occur */ - uReturn = uResult; - goto Done; - } - if(uResult == QCBOR_ERR_NO_MORE_ITEMS) { - /* Unexpected end of map or array. */ - uReturn = uResult; - goto Done; - } - /* See if item has one of the labels that are of interest */ - bool bMatched = false; - for(int nIndex = 0; pItemArray[nIndex].uLabelType != QCBOR_TYPE_NONE; nIndex++) { - if(QCBORItem_MatchLabel(Item, pItemArray[nIndex])) { - /* A label match has been found */ - if(uFoundItemBitMap & (0x01ULL << nIndex)) { - uReturn = QCBOR_ERR_DUPLICATE_LABEL; - goto Done; - } - if(uResult != QCBOR_SUCCESS) { - /* The label matches, but the data item is in error. - * It is OK to have recoverable errors on items that - * are not matched. */ - uReturn = uResult; - goto Done; - } - if(!QCBORItem_MatchType(Item, pItemArray[nIndex])) { - /* The data item is not of the type(s) requested */ - uReturn = QCBOR_ERR_UNEXPECTED_TYPE; - goto Done; - } - /* Successful match. Return the item. */ - pItemArray[nIndex] = Item; - uFoundItemBitMap |= 0x01ULL << nIndex; - if(pInfo) { - pInfo->uStartOffset = uOffset; - } - bMatched = true; - } - } + /* Skip out if not an indefinite-length string */ + const uint8_t uStringType = pDecodedItem->uDataType; + if(uStringType != QCBOR_TYPE_BYTE_STRING && + uStringType != QCBOR_TYPE_TEXT_STRING) { + goto Done; + } + if(pDecodedItem->val.string.len != QCBOR_STRING_LENGTH_INDEFINITE) { + goto Done; + } +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS + /* Can't decode indefinite-length strings without a string allocator */ + if(!pMe->StringAllocator.pfAllocator) { + uReturn = QCBOR_ERR_NO_STRING_ALLOCATOR; + goto Done; + } - if(!bMatched && pCallBack != NULL) { - /* - Call the callback on unmatched labels. - (It is tempting to do duplicate detection here, but that would - require dynamic memory allocation because the number of labels - that might be encountered is unbounded.) - */ - uReturn = (*(pCallBack->pfCallback))(pCallBack->pCBContext, &Item); - if(uReturn != QCBOR_SUCCESS) { - goto Done; - } - } + /* Loop getting chunks of the indefinite-length string */ + UsefulBufC FullString = NULLUsefulBufC; - /* - Consume the item whether matched or not. This - does the work of traversing maps and array and - everything in them. In this loop only the - items at the current nesting level are examined - to match the labels. + for(;;) { + /* Get QCBORItem for next chunk */ + QCBORItem StringChunkItem; + /* Pass false to DecodeAtomicDataItem() because the individual + * string chunks in an indefinite-length must not be + * allocated. They are always copied into the allocated + * contiguous buffer allocated here. */ - uReturn = QCBORDecode_Private_ConsumeItem(pMe, &Item, NULL, &uNextNestLevel); - if(uReturn != QCBOR_SUCCESS) { - goto Done; + uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, false, &StringChunkItem); + if(uReturn) { + break; } - if(pInfo) { - pInfo->uItemCount++; + /* Is item is the marker for end of the indefinite-length string? */ + if(StringChunkItem.uDataType == QCBOR_TYPE_BREAK) { + /* String is complete */ + pDecodedItem->val.string = FullString; + pDecodedItem->uDataAlloc = 1; + break; } - } while (uNextNestLevel >= uMapNestLevel); - - uReturn = QCBOR_SUCCESS; + /* All chunks must be of the same type, the type of the item + * that introduces the indefinite-length string. This also + * catches errors where the chunk is not a string at all and an + * indefinite-length string inside an indefinite-length string. + */ + if(StringChunkItem.uDataType != uStringType || + StringChunkItem.val.string.len == QCBOR_STRING_LENGTH_INDEFINITE) { + uReturn = QCBOR_ERR_INDEFINITE_STRING_CHUNK; + break; + } - const size_t uEndOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); + if (StringChunkItem.val.string.len > 0) { + /* The first time throurgh FullString.ptr is NULL and this is + * equivalent to StringAllocator_Allocate(). Subsequently it is + * not NULL and a reallocation happens. + */ + UsefulBuf NewMem = StringAllocator_Reallocate(&(pMe->StringAllocator), + FullString.ptr, + FullString.len + StringChunkItem.val.string.len); + if(UsefulBuf_IsNULL(NewMem)) { + uReturn = QCBOR_ERR_STRING_ALLOCATE; + break; + } - // Check here makes sure that this won't accidentally be - // QCBOR_MAP_OFFSET_CACHE_INVALID which is larger than - // QCBOR_MAX_DECODE_INPUT_SIZE. - // Cast to uint32_t to possibly address cases where SIZE_MAX < UINT32_MAX - if((uint32_t)uEndOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) { - uReturn = QCBOR_ERR_INPUT_TOO_LARGE; - goto Done; - } - /* Cast OK because encoded CBOR is limited to UINT32_MAX */ - pMe->uMapEndOffsetCache = (uint32_t)uEndOffset; - - 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 */ - for(int i = 0; pItemArray[i].uLabelType != 0; i++) { - if(!(uFoundItemBitMap & (0x01ULL << i))) { - pItemArray[i].uDataType = QCBOR_TYPE_NONE; - pItemArray[i].uLabelType = QCBOR_TYPE_NONE; + /* Copy new string chunk to the end of accumulated string */ + FullString = UsefulBuf_CopyOffset(NewMem, FullString.len, StringChunkItem.val.string); } } + if(uReturn != QCBOR_SUCCESS && !UsefulBuf_IsNULLC(FullString)) { + /* Getting the item failed, clean up the allocated memory */ + StringAllocator_Free(&(pMe->StringAllocator), FullString.ptr); + } +#else /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ + uReturn = QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED; +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ + +Done: return uReturn; } -/* - * Public function, see header qcbor/qcbor_decode.h file +#ifndef QCBOR_DISABLE_TAGS +/** + * @brief This converts a tag number to a shorter mapped value for storage. + * + * @param[in] pMe The decode context. + * @param[in] uUnMappedTag The tag number to map + * @param[out] puMappedTagNumber The stored tag number. + * + * @return error code. + * + * The main point of mapping tag numbers is make QCBORItem + * smaller. With this mapping storage of 4 tags takes up 8 + * bytes. Without, it would take up 32 bytes. + * + * This maps tag numbers greater than QCBOR_LAST_UNMAPPED_TAG. + * QCBOR_LAST_UNMAPPED_TAG is a little smaller than MAX_UINT16. + * + * See also UnMapTagNumber() and @ref QCBORItem. */ -void -QCBORDecode_SeekToLabelN(QCBORDecodeContext *pMe, int64_t nLabel) +static QCBORError +QCBORDecode_Private_MapTagNumber(QCBORDecodeContext *pMe, + const uint64_t uUnMappedTag, + uint16_t *puMappedTagNumber) { - MapSearchInfo Info; - QCBORItem OneItemSeach[2]; - - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } + if(uUnMappedTag > QCBOR_LAST_UNMAPPED_TAG) { + unsigned uTagMapIndex; + /* Is there room in the tag map, or is it in it already? */ + for(uTagMapIndex = 0; uTagMapIndex < QCBOR_NUM_MAPPED_TAGS; uTagMapIndex++) { + if(pMe->auMappedTags[uTagMapIndex] == CBOR_TAG_INVALID64) { + break; + } + if(pMe->auMappedTags[uTagMapIndex] == uUnMappedTag) { + break; + } + } + if(uTagMapIndex >= QCBOR_NUM_MAPPED_TAGS) { + return QCBOR_ERR_TOO_MANY_TAGS; + } - OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; - OneItemSeach[0].label.int64 = nLabel; - OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + /* Covers the cases where tag is new and were it is already in the map */ + pMe->auMappedTags[uTagMapIndex] = uUnMappedTag; + *puMappedTagNumber = (uint16_t)(uTagMapIndex + QCBOR_LAST_UNMAPPED_TAG + 1); - pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); - if(pMe->uLastError == QCBOR_SUCCESS) { - UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset); + } else { + *puMappedTagNumber = (uint16_t)uUnMappedTag; } + + return QCBOR_SUCCESS; } -void -QCBORDecode_SeekToLabelSZ(QCBORDecodeContext *pMe, const char *szLabel) +/** + * @brief This converts a mapped tag number to the actual tag number. + * + * @param[in] pMe The decode context. + * @param[in] uMappedTagNumber The stored tag number. + * + * @return The actual tag number is returned or + * @ref CBOR_TAG_INVALID64 on error. + * + * This is the reverse of MapTagNumber() + */ +uint64_t +QCBORDecode_Private_UnMapTagNumber(const QCBORDecodeContext *pMe, + const uint16_t uMappedTagNumber) { -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - MapSearchInfo Info; - QCBORItem OneItemSeach[2]; - - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } - - OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; - OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); - OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array - - pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); - if(pMe->uLastError == QCBOR_SUCCESS) { - UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset); + if(uMappedTagNumber <= QCBOR_LAST_UNMAPPED_TAG) { + return uMappedTagNumber; + } else if(uMappedTagNumber == CBOR_TAG_INVALID16) { + return CBOR_TAG_INVALID64; + } else { + /* This won't be negative because of code below in + * MapTagNumber() + */ + const unsigned uIndex = uMappedTagNumber - (QCBOR_LAST_UNMAPPED_TAG + 1); + return pMe->auMappedTags[uIndex]; } -#else - (void)pMe; - (void)szLabel; - pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ } -void -QCBORDecode_Private_GetItemInMapNoCheck(QCBORDecodeContext *pMe, - QCBORItem *OneItemSeach, - QCBORItem *pItem, - size_t *puOffset) +static const struct QCBORTagDecoderEntry * +QCBORDecode_Private_LookUpTagDecoder(const struct QCBORTagDecoderEntry *pTagContentTable, + const uint64_t uTagNumber) { - QCBORError uErr; - MapSearchInfo SearchInfo; - - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } - - uErr = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &SearchInfo, NULL); + const struct QCBORTagDecoderEntry *pTE; - if(uErr == QCBOR_SUCCESS && OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) { - uErr = QCBOR_ERR_LABEL_NOT_FOUND; + if(pTagContentTable == NULL) { + return NULL; } - *pItem = OneItemSeach[0]; - *puOffset = SearchInfo.uStartOffset; - if(uErr == QCBOR_SUCCESS) { - QCBORDecode_Private_SaveTagNumbers(pMe, pItem); + for(pTE = pTagContentTable; pTE->uTagNumber != CBOR_TAG_INVALID64; pTE++) { + if(pTE->uTagNumber == uTagNumber || pTE->uTagNumber == CBOR_TAG_ANY) { + break; + } } - pMe->uLastError = (uint8_t)uErr; -} - - -static void -QCBORDecode_Private_GetItemInMap(QCBORDecodeContext *pMe, QCBORItem *OneItemSeach, QCBORItem *pItem) -{ - QCBORError uErr; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, &uOffset); - - uErr = QCBORDecode_Private_GetItemChecks(pMe, pMe->uLastError, uOffset, pItem); - if(uErr != QCBOR_SUCCESS) { - goto Done; + if(pTE->uTagNumber == CBOR_TAG_INVALID64) { + return NULL; } - QCBORDecode_Private_SaveTagNumbers(pMe, pItem); - -Done: - pMe->uLastError = (uint8_t)uErr; -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetItemInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uQcborType, - QCBORItem *pItem) -{ - QCBORItem OneItemSeach[2]; - - OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; - OneItemSeach[0].label.int64 = nLabel; - OneItemSeach[0].uDataType = uQcborType; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array - - QCBORDecode_Private_GetItemInMap(pMe, OneItemSeach, pItem); + return pTE; } +#endif /* ! QCBOR_DISABLE_TAGS */ /** - * @brief Get an item by label by type. + * @brief Aggregate all tags wrapping a data item (decode layer 4). * - * @param[in] pMe The decode context. - * @param[in] nLabel The label to search map for. - * @param[in] uQcborType The QCBOR type to look for. - * @param[out] pItem The item found. - * @param[out] puOffset The offset of item for tag consumption check. + * @param[in] pMe Decoder context + * @param[out] pDecodedItem The decoded item that work is done on. + * + * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved + * features + * @retval QCBOR_ERR_HIT_END Unexpected end of input + * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered + * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. + * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. + * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode + * of half-precision disabled + * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all + * float decode is disabled. + * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of + * simple type in input. + * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array + * in input, but indefinite + * lengths disabled. + * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, + * but no string allocator. + * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. + * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in + * input, but indefinite-length + * strings are disabled. + * @retval QCBOR_ERR_TOO_MANY_TAGS Too many tag numbers on item. * - * This finds the item with the given label in currently open - * map. This does not call QCBORDecode_Private_GetItemChecks() - * to check tag number consumption or decode conformance. + * This loops getting atomic data items until one is not a tag + * number. Usually this is largely pass-through because most + * item are not tag numbers. */ -void -QCBORDecode_Private_GetItemInMapNoCheckN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uQcborType, - QCBORItem *pItem, - size_t *puOffset) +static QCBORError +QCBORDecode_Private_GetNextTagNumber(QCBORDecodeContext *pMe, + QCBORItem *pDecodedItem) { - QCBORItem OneItemSeach[2]; +#ifndef QCBOR_DISABLE_TAGS + int nIndex; + QCBORError uErr; + uint16_t uMappedTagNumber; + QCBORError uReturn; - OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; - OneItemSeach[0].label.int64 = nLabel; - OneItemSeach[0].uDataType = uQcborType; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + /* Accummulate the tag numbers from multiple items here and then + * copy them into the last item, the non-tag-number item. + */ + QCBORMappedTagNumbers auTagNumbers;; - QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, puOffset); -} + /* Initialize to CBOR_TAG_INVALID16 */ + #if CBOR_TAG_INVALID16 != 0xffff + /* Be sure the memset is initializing to CBOR_TAG_INVALID16 */ + #err CBOR_TAG_INVALID16 tag not defined as expected + #endif + memset(auTagNumbers, 0xff, sizeof(auTagNumbers)); + /* Loop fetching data items until the item fetched is not a tag number */ + uReturn = QCBOR_SUCCESS; + for(nIndex = 0; ; nIndex++) { + uErr = QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem); + if(uErr != QCBOR_SUCCESS) { + uReturn = uErr; + break; + } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uQcborType, - QCBORItem *pItem) -{ -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - QCBORItem OneItemSeach[2]; + if(pDecodedItem->uDataType != QCBOR_TYPE_TAG_NUMBER) { + /* Successful exit from loop; maybe got some tags, maybe not */ + memcpy(pDecodedItem->auTagNumbers, auTagNumbers, sizeof(auTagNumbers)); + break; + } - OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; - OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); - OneItemSeach[0].uDataType = uQcborType; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + if(nIndex >= QCBOR_MAX_TAGS_PER_ITEM) { + /* No room in the item's tag number array */ + uReturn = QCBOR_ERR_TOO_MANY_TAGS; + /* Continue on to get all tag numbers wrapping this item even + * though it is erroring out in the end. This allows decoding + * to continue. This is a QCBOR resource limit error, not a + * problem with being well-formed CBOR. + */ + continue; + } - QCBORDecode_Private_GetItemInMap(pMe, OneItemSeach, pItem); + /* Map the tag number */ + uMappedTagNumber = 0; + uReturn = QCBORDecode_Private_MapTagNumber(pMe, pDecodedItem->val.uTagNumber, &uMappedTagNumber); + /* Continue even on error so as to consume all tag numbers + * wrapping this data item so decoding can go on. If + * QCBORDecode_Private_MapTagNumber() errors once it will + * continue to error. + */ -#else - (void)pMe; - (void)szLabel; - (void)uQcborType; - (void)pItem; - pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ -} + auTagNumbers[nIndex] = uMappedTagNumber; + } -/** - * @brief Get an item by string label of a particular type - * - * @param[in] pMe The decode context. - * @param[in] szLabel The label to search map for. - * @param[in] uQcborType The QCBOR type to look for. - * @param[out] pItem The item found. - * @param[out] puOffset The offset of item for tag consumption check. - * - * This finds the item with the given label in currently open - * map. This does not call QCBORDecode_Private_GetItemChecks() - * to check tag number consumption or decode conformance. - */ -void -QCBORDecode_Private_GetItemInMapNoCheckSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uQcborType, - QCBORItem *pItem, - size_t *puOffset) -{ -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - QCBORItem OneItemSeach[2]; + return uReturn; - OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; - OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); - OneItemSeach[0].uDataType = uQcborType; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array +#else /* ! QCBOR_DISABLE_TAGS */ - QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, puOffset); + return QCBORDecode_Private_GetNextFullString(pMe, pDecodedItem); -#else - (void)pMe; - (void)szLabel; - (void)uQcborType; - (void)pItem; - (void)puOffset; - pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +#endif /* ! QCBOR_DISABLE_TAGS */ } - - /** - * @brief Semi-private. Get pointer, length and item for an array or map. + * @brief Combine a map entry label and value into one item (decode layer 3). * - * @param[in] pMe The decode context. - * @param[in] uType CBOR major type, either array/map. - * @param[out] pItem The item for the array/map. - * @param[out] pEncodedCBOR Pointer and length of the encoded map or array. + * @param[in] pMe Decoder context + * @param[out] pDecodedItem The decoded item that work is done on. * - * The next item to be decoded must be a map or array as specified by @c uType. + * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved + * features + * @retval QCBOR_ERR_HIT_END Unexpected end of input + * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered + * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. + * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. + * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode + * of half-precision disabled + * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all + * float decode is disabled. + * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of + * simple type in input. + * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array + * in input, but indefinite + * lengths disabled. + * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, + * but no string allocator. + * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. + * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in + * input, but indefinite-length + * strings are disabled. + * @retval QCBOR_ERR_TOO_MANY_TAGS Too many tag numbers on item. + * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array. + * @retval QCBOR_ERR_MAP_LABEL_TYPE Map label not string or integer. * - * @c pItem will be filled in with the label and tags of the array or map - * in addition to @c pEncodedCBOR giving the pointer and length of the - * encoded CBOR. + * If the current nesting level is a map, then this combines pairs of + * items into one data item with a label and value. * - * When this is complete, the traversal cursor is at the end of the array or - * map that was retrieved. + * This is passthrough if the current nesting level is not a map. + * + * This also implements maps-as-array mode where a map is treated like + * an array to allow caller to do their own label processing. */ -void -QCBORDecode_Private_GetArrayOrMap(QCBORDecodeContext *pMe, - const uint8_t uType, - QCBORItem *pItem, - UsefulBufC *pEncodedCBOR) -{ - QCBORError uErr; - uint8_t uNestLevel; - size_t uStartingCursor; - size_t uStartOfReturned; - size_t uEndOfReturned; - size_t uTempSaveCursor; - bool bInMap; +static QCBORError +QCBORDecode_Private_GetNextMapEntry(QCBORDecodeContext *pMe, + QCBORItem *pDecodedItem, + uint32_t *puLabelEndOffset) +{ QCBORItem LabelItem; - bool EndedByBreak; - - uStartingCursor = UsefulInputBuf_Tell(&(pMe->InBuf)); - bInMap = DecodeNesting_IsCurrentTypeMap(&(pMe->nesting)); - - /* Could call GetNext here, but don't need to because this - * is only interested in arrays and maps. TODO: switch to GetNext()? */ - uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem, NULL); - if(uErr != QCBOR_SUCCESS) { - pMe->uLastError = (uint8_t)uErr; - return; - } + QCBORError uErr, uErr2; - uint8_t uItemDataType = pItem->uDataType; -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) { - uItemDataType = QCBOR_TYPE_ARRAY; + uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem); + if(QCBORDecode_IsUnrecoverableError(uErr)) { + goto Done; } -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - if(uItemDataType != uType) { - pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; - return; + if(!DecodeNesting_IsCurrentTypeMap(&(pMe->nesting))) { + /* Not decoding a map. Nothing to do. */ + /* When decoding maps-as-arrays, the type will be + * QCBOR_TYPE_MAP_AS_ARRAY and this function will exit + * here. This is now map processing for maps-as-arrays is not + * done. */ + goto Done; } - if(bInMap) { - /* If the item is in a map, the start of the array/map - * itself, not the label, must be found. Do this by - * rewinding to the starting position and fetching - * just the label data item. QCBORDecode_Private_GetNextTagNumber() - * doesn't do any of the array/map item counting or nesting - * level tracking. Used here it will just fetech the label - * data item. - * - * Have to save the cursor and put it back to the position - * after the full item once the label as been fetched by - * itself. - */ - uTempSaveCursor = UsefulInputBuf_Tell(&(pMe->InBuf)); - UsefulInputBuf_Seek(&(pMe->InBuf), uStartingCursor); + /* Decoding a map entry, so the item decoded above was the label */ + LabelItem = *pDecodedItem; - /* Item has been fetched once so safe to ignore error */ - (void)QCBORDecode_Private_GetNextTagNumber(pMe, &LabelItem); + if(puLabelEndOffset != NULL) { + /* Cast is OK because lengths are all 32-bit in QCBOR */ + *puLabelEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf)); + } - uStartOfReturned = UsefulInputBuf_Tell(&(pMe->InBuf)); - UsefulInputBuf_Seek(&(pMe->InBuf), uTempSaveCursor); - } else { - uStartOfReturned = uStartingCursor; + /* Get the value of the map item */ + uErr2 = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem); + if(QCBORDecode_IsUnrecoverableError(uErr2)) { + uErr = uErr2; + goto Done; + } + if(uErr2 != QCBOR_SUCCESS) { + /* The recoverable error for the value overrides the recoverable + * error for the label, if there was an error for the label */ + uErr = uErr2; } - /* Consume the entire array/map to find the end */ - uErr = QCBORDecode_Private_ConsumeItem(pMe, pItem, &EndedByBreak, &uNestLevel); - if(uErr != QCBOR_SUCCESS) { - pMe->uLastError = (uint8_t)uErr; + /* Combine the label item and value item into one */ + pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc; + pDecodedItem->uLabelType = LabelItem.uDataType; + +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + /* TODO: QCBOR_DECODE_MODE_MAP_STRINGS_ONLY might have been a bad idea. Maybe + * get rid of it in QCBOR 2.0 + */ + if(pMe->uDecodeMode & QCBOR_DECODE_MODE_MAP_STRINGS_ONLY && + LabelItem.uDataType != QCBOR_TYPE_TEXT_STRING) { + uErr = QCBOR_ERR_MAP_LABEL_TYPE; goto Done; } +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ - /* Fill in returned values */ - uEndOfReturned = UsefulInputBuf_Tell(&(pMe->InBuf)); - if(EndedByBreak) { - /* When ascending nesting levels, a break for the level above - * was consumed. That break is not a part of what is consumed here. */ - uEndOfReturned--; + switch(LabelItem.uDataType) { + case QCBOR_TYPE_INT64: + pDecodedItem->label.int64 = LabelItem.val.int64; + break; + +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + case QCBOR_TYPE_UINT64: + pDecodedItem->label.uint64 = LabelItem.val.uint64; + break; + + case QCBOR_TYPE_TEXT_STRING: + case QCBOR_TYPE_BYTE_STRING: + pDecodedItem->label.string = LabelItem.val.string; + break; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + + default: + /* It is possible to skip over labels that are non-aggregate + * types like floats, but not to skip over labels that are + * arrays or maps. We might eventually handle more label + * types like floats as they are not too hard and we now + * have QCBOR_DISABLE_NON_INTEGER_LABELS */ + if(!pMe->bAllowAllLabels || QCBORItem_IsMapOrArray(LabelItem)) { + uErr = QCBOR_ERR_MAP_LABEL_TYPE; + goto Done; + } } - pEncodedCBOR->ptr = UsefulInputBuf_OffsetToPointer(&(pMe->InBuf), uStartOfReturned); - pEncodedCBOR->len = uEndOfReturned - uStartOfReturned; Done: - return; + return uErr; } +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS /** - * @brief Semi-private. Get pointer, length and item count of an array or map. + * @brief Peek and see if next data item is a break; * - * @param[in] pMe The decode context. - * @param[in] pTarget The label and type of the array or map to retrieve. - * @param[out] pItem The item for the array/map. - * @param[out] pEncodedCBOR Pointer and length of the encoded map or array. + * param[in] pUIB UsefulInputBuf to read from. + * @param[out] pbNextIsBreak Indicate if next was a break or not. * - * The next item to be decoded must be a map or array as specified by @c uType. + * @return Any decoding error. * - * When this is complete, the traversal cursor is unchanged. - */void -QCBORDecode_Private_SearchAndGetArrayOrMap(QCBORDecodeContext *pMe, - QCBORItem *pTarget, - QCBORItem *pItem, - UsefulBufC *pEncodedCBOR) + * See if next item is a CBOR break. If it is, it is consumed, + * if not it is not consumed. +*/ +static QCBORError +QCBOR_Private_NextIsBreak(QCBORDecodeContext *pMe, bool *pbNextIsBreak) { - MapSearchInfo Info; - QCBORDecodeNesting SaveNesting; - size_t uSaveCursor; - - pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pTarget, &Info, NULL); - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } - pMe->uLastError = (uint8_t)QCBORDecode_Private_GetItemChecks(pMe, pMe->uLastError, Info.uStartOffset, pItem); - if(pMe->uLastError != QCBOR_SUCCESS) { - return; + *pbNextIsBreak = false; + if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf)) != 0) { + QCBORItem Peek; + size_t uPeek = UsefulInputBuf_Tell(&(pMe->InBuf)); + QCBORError uReturn = QCBOR_Private_DecodeAtomicDataItem(pMe, false, &Peek); + if(uReturn != QCBOR_SUCCESS) { + return uReturn; + } + if(Peek.uDataType != QCBOR_TYPE_BREAK) { + /* It is not a break, rewind so it can be processed normally. */ + UsefulInputBuf_Seek(&(pMe->InBuf), uPeek); + } else { + *pbNextIsBreak = true; + } } - /* Save the whole position of things so they can be restored. - * so the cursor position is unchanged by this operation, like - * all the other GetXxxxInMap() operations. */ - DecodeNesting_PrepareForMapSearch(&(pMe->nesting), &SaveNesting); - uSaveCursor = UsefulInputBuf_Tell(&(pMe->InBuf)); - - DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting)); - UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset); - QCBORDecode_Private_GetArrayOrMap(pMe, pTarget[0].uDataType, pItem, pEncodedCBOR); - - UsefulInputBuf_Seek(&(pMe->InBuf), uSaveCursor); - DecodeNesting_RestoreFromMapSearch(&(pMe->nesting), &SaveNesting); + return QCBOR_SUCCESS; } +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ - - -static void -QCBORDecode_Private_ProcessTagOne(QCBORDecodeContext *pMe, - QCBORItem *pItem, - const uint8_t uTagRequirement, - const uint8_t uQCBORType, - const uint64_t uTagNumber, - QCBORTagContentCallBack *pfCB, - size_t uOffset); - /** - * @brief Semi-private to get an string by label to match a tag specification. + * @brief Ascend up nesting levels if all items in them have been consumed. * - * @param[in] pMe The decode context. - * @param[in] nLabel Label to search map for. - * @param[in] uTagRequirement Whether or not tag number is required. - * See @ref QCBOR_TAG_REQUIREMENT_TAG. - * @param[in] uQCBOR_Type QCBOR type to search for. - * @param[in] uTagNumber Tag number to match. - * @param[out] pString The string found. + * @param[in] pMe The decode context. + * @param[in] bMarkEnd If true mark end of maps/arrays with count of zero. + * @param[out] pbBreak Set to true if extra break was consumed. * - * This finds the string with the given label in currently open - * map. Then checks that its tag number and types matches the tag - * specification. If not, an error is set in the decode context. - */ -void -QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - const uint8_t uQCBOR_Type, - const uint64_t uTagNumber, - UsefulBufC *pString) -{ - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - uQCBOR_Type, - uTagNumber, - QCBORDecode_StringsTagCB, - uOffset); - - if(pMe->uLastError == QCBOR_SUCCESS) { - *pString = Item.val.string; - } -} - - -/** - * @brief Semi-private to get an string by label to match a tag specification. + * An item was just consumed, now figure out if it was the + * end of an array/map map that can be closed out. That + * may in turn close out the above array/map... * - * @param[in] pMe The decode context. - * @param[in] szLabel Label to search map for. - * @param[in] uTagRequirement Whether or not tag number is required. - * See @ref QCBOR_TAG_REQUIREMENT_TAG. - * @param[in] uQCBOR_Type QCBOR type to search for. - * @param[in] uTagNumber Tag number to match. - * @param[out] pString The string found. + * When ascending indefinite-length arrays and maps, this will correctly + * consume the break for the level above. This is a problem for the + * implementation of QCBORDecode_GetArray() that must not return + * that break. @c pbBreak is set to true to indicate that one + * byte should be removed. * - * This finds the string with the given label in currently open - * map. Then checks that its tag number and types matches the tag - * specification. If not, an error is set in the decode context. - */ -void -QCBORDecode_Private_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - uint8_t uTagRequirement, - uint8_t uQCBOR_Type, - uint64_t uTagNumber, - UsefulBufC *pString) + * Improvement: this could reduced further if indef is disabled + */ +QCBORError +QCBORDecode_Private_NestLevelAscender(QCBORDecodeContext *pMe, bool bMarkEnd, bool *pbBreak) { - QCBORItem Item; - size_t uOffset; + QCBORError uReturn; - QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - uQCBOR_Type, - uTagNumber, - QCBORDecode_StringsTagCB, - uOffset); + /* Loop ascending nesting levels as long as there is ascending to do */ + while(!DecodeNesting_IsCurrentAtTop(&(pMe->nesting))) { + if(pbBreak) { + *pbBreak = false; + } + if(DecodeNesting_IsCurrentBstrWrapped(&(pMe->nesting))) { + /* Nesting level is bstr-wrapped CBOR */ - if(pMe->uLastError == QCBOR_SUCCESS) { - *pString = Item.val.string; - } -} + /* Ascent for bstr-wrapped CBOR is always by explicit call + * so no further ascending can happen. + */ + break; + } else if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) { + /* Level is a definite-length array/map */ -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemList) -{ - pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, NULL); -} + /* Decrement the item count the definite-length array/map */ + DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(&(pMe->nesting)); + if(!DecodeNesting_IsEndOfDefiniteLengthMapOrArray(&(pMe->nesting))) { + /* Didn't close out array/map, so all work here is done */ + break; + } + /* All items in a definite-length array were consumed so it + * is time to ascend one level. This happens below. + */ -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetItemsInMapWithCallback(QCBORDecodeContext *pMe, - QCBORItem *pItemList, - void *pCallbackCtx, - QCBORItemCallback pfCB) -{ - MapSearchCallBack CallBack; +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS + } else { + /* Level is an indefinite-length array/map. */ + + /* Check for a break which is what ends indefinite-length arrays/maps */ + bool bIsBreak = false; + uReturn = QCBOR_Private_NextIsBreak(pMe, &bIsBreak); + if(uReturn != QCBOR_SUCCESS) { + goto Done; + } - CallBack.pCBContext = pCallbackCtx; - CallBack.pfCallback = pfCB; + if(!bIsBreak) { + /* Not a break so array/map does not close out. All work is done */ + break; + } - pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, &CallBack); -} + /* It was a break in an indefinitelength map / array so + * it is time to ascend one level. + */ + if(pbBreak) { + *pbBreak = true; + } +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ + } -#ifndef QCBOR_DISABLE_TAGS -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -QCBORError -QCBORDecode_GetNextTagNumberInMapN(QCBORDecodeContext *pMe, const int64_t nLabel, uint64_t *puTagNumber) -{ - size_t uOffset; - MapSearchInfo Info; - QCBORItem OneItemSeach[2]; - if(pMe->uLastError != QCBOR_SUCCESS) { - return pMe->uLastError; - } + /* All items in the array/map have been consumed. */ - OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; - OneItemSeach[0].label.int64 = nLabel; - OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + /* But ascent in bounded mode is only by explicit call to + * QCBORDecode_ExitBoundedMode(). + */ + if(DecodeNesting_IsCurrentBounded(&(pMe->nesting))) { + /* Set the count to zero for definite-length arrays to indicate + * cursor is at end of bounded array/map */ + if(bMarkEnd) { + /* Used for definite and indefinite to signal end */ + DecodeNesting_ZeroMapOrArrayCount(&(pMe->nesting)); - QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); + } + break; + } - uOffset = Info.uStartOffset; - if(uOffset == pMe->uTagNumberCheckOffset) { - pMe->uTagNumberIndex++; - } else { - pMe->uTagNumberIndex = 0; + /* Finally, actually ascend one level. */ + DecodeNesting_Ascend(&(pMe->nesting)); } - *puTagNumber = CBOR_TAG_INVALID64; + uReturn = QCBOR_SUCCESS; - *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex); - if(*puTagNumber == CBOR_TAG_INVALID64 || - QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) { - pMe->uTagNumberIndex = QCBOR_ALL_TAGS_PROCESSED; - } - pMe->uTagNumberCheckOffset = uOffset; +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS +Done: +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */ return uReturn; } -/* - * Public function, see header qcbor/qcbor_decode.h file +/** + * @brief Ascending & Descending out of nesting levels (decode layer 2). + * + * @param[in] pMe Decoder context + * @param[out] pbBreak Set to true if extra break was consumed. + * @param[out] pDecodedItem The decoded item that work is done on. + + * @retval QCBOR_ERR_UNSUPPORTED Encountered unsupported/reserved + * features + * @retval QCBOR_ERR_HIT_END Unexpected end of input + * @retval QCBOR_ERR_INT_OVERFLOW Too-large negative encountered + * @retval QCBOR_ERR_STRING_ALLOCATE Out of memory. + * @retval QCBOR_ERR_STRING_TOO_LONG String longer than SIZE_MAX - 4. + * @retval QCBOR_ERR_HALF_PRECISION_DISABLED Half-precision in input, but decode + * of half-precision disabled + * @retval QCBOR_ERR_ALL_FLOAT_DISABLED Float-point in input, but all + * float decode is disabled. + * @retval QCBOR_ERR_BAD_TYPE_7 Not-allowed representation of + * simple type in input. + * @retval QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED Indefinite length map/array + * in input, but indefinite + * lengths disabled. + * @retval QCBOR_ERR_NO_STRING_ALLOCATOR Indefinite-length string in input, + * but no string allocator. + * @retval QCBOR_ERR_INDEFINITE_STRING_CHUNK Error in indefinite-length string. + * @retval QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED Indefinite-length string in + * input, but indefinite-length + * strings are disabled. + * @retval QCBOR_ERR_TOO_MANY_TAGS Too many tag numbers on item. + * @retval QCBOR_ERR_ARRAY_DECODE_TOO_LONG Too many items in array. + * @retval QCBOR_ERR_MAP_LABEL_TYPE Map label not string or integer. + * @retval QCBOR_ERR_NO_MORE_ITEMS Need more items for map or array. + * @retval QCBOR_ERR_BAD_BREAK Indefinite-length break in wrong + * place. + * @retval QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP Nesting deeper than QCBOR + * can handle. + * + * This handles the traversal descending into and asecnding out of + * maps, arrays and bstr-wrapped CBOR. It figures out the ends of + * definite- and indefinte-length maps and arrays by looking at the + * item count or finding CBOR breaks. It detects the ends of the + * top-level sequence and of bstr-wrapped CBOR by byte count. */ QCBORError -QCBORDecode_GetNextTagNumberInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, uint64_t *puTagNumber) +QCBORDecode_Private_GetNextMapOrArray(QCBORDecodeContext *pMe, + bool *pbBreak, + QCBORItem *pDecodedItem, + uint32_t *puLabelEndOffset) { -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - size_t uOffset; - MapSearchInfo Info; - QCBORItem OneItemSeach[2]; + QCBORError uReturn; + /* ==== First: figure out if at the end of a traversal ==== */ - if(pMe->uLastError != QCBOR_SUCCESS) { - return pMe->uLastError; + /* If out of bytes to consume, it is either the end of the + * top-level sequence of some bstr-wrapped CBOR that was entered. + * + * In the case of bstr-wrapped CBOR, the length of the + * UsefulInputBuf was set to that of the bstr-wrapped CBOR. When + * the bstr-wrapped CBOR is exited, the length is set back to the + * top-level's length or to the next highest bstr-wrapped CBOR. + */ + if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf)) == 0) { + uReturn = QCBOR_ERR_NO_MORE_ITEMS; + goto Done; + } + + /* Check to see if at the end of a bounded definite-length map or + * array. The check for a break ending indefinite-length array is + * later in QCBORDecode_NestLevelAscender(). + */ + if(DecodeNesting_IsAtEndOfBoundedLevel(&(pMe->nesting))) { + uReturn = QCBOR_ERR_NO_MORE_ITEMS; + goto Done; } - OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; - OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); - OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; - OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + /* ==== Next: not at the end, so get another item ==== */ + uReturn = QCBORDecode_Private_GetNextMapEntry(pMe, pDecodedItem, puLabelEndOffset); + if(QCBORDecode_IsUnrecoverableError(uReturn)) { + /* Error is so bad that traversal is not possible. */ + goto Done; + } - QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); + /* Record the nesting level for this data item before processing + * any of decrementing and descending. + */ + pDecodedItem->uNestingLevel = DecodeNesting_GetCurrentLevel(&(pMe->nesting)); - uOffset = Info.uStartOffset; - if(uOffset == pMe->uTagNumberCheckOffset) { - pMe->uTagNumberIndex++; - } else { - pMe->uTagNumberIndex = 0; + /* ==== Next: Process the item for descent, ascent, decrement... ==== */ + if(QCBORItem_IsMapOrArray(*pDecodedItem)) { + /* If the new item is a map or array, descend. + * + * Empty indefinite-length maps and arrays are descended into, + * but then ascended out of in the next chunk of code. + * + * Maps and arrays do count as items in the map/array that + * encloses them so a decrement needs to be done for them too, + * but that is done only when all the items in them have been + * processed, not when they are opened with the exception of an + * empty map or array. + */ + QCBORError uDescendErr; + uDescendErr = DecodeNesting_DescendMapOrArray(&(pMe->nesting), + pDecodedItem->uDataType, + pDecodedItem->val.uCount); + if(uDescendErr != QCBOR_SUCCESS) { + /* This error is probably a traversal error and it overrides + * the non-traversal error. + */ + uReturn = uDescendErr; + goto Done; + } } - *puTagNumber = CBOR_TAG_INVALID64; + if(!QCBORItem_IsMapOrArray(*pDecodedItem) || + QCBORItem_IsEmptyDefiniteLengthMapOrArray(*pDecodedItem) || + QCBORItem_IsIndefiniteLengthMapOrArray(*pDecodedItem)) { + /* The following cases are handled here: + * - A non-aggregate item like an integer or string + * - An empty definite-length map or array + * - An indefinite-length map or array that might be empty or might not. + * + * QCBORDecode_NestLevelAscender() does the work of decrementing the count + * for an definite-length map/array and break detection for an + * indefinite-0length map/array. If the end of the map/array was + * reached, then it ascends nesting levels, possibly all the way + * to the top level. + */ + QCBORError uAscendErr; + uAscendErr = QCBORDecode_Private_NestLevelAscender(pMe, true, pbBreak); + if(uAscendErr != QCBOR_SUCCESS) { + /* This error is probably a traversal error and it overrides + * the non-traversal error. + */ + uReturn = uAscendErr; + goto Done; + } + } - *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex); - if(*puTagNumber == CBOR_TAG_INVALID64 || - QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) { - pMe->uTagNumberIndex = 255; /* All tags clear for this item */ + /* ==== Last: tell the caller the nest level of the next item ==== */ + /* Tell the caller what level is next. This tells them what + * maps/arrays were closed out and makes it possible for them to + * reconstruct the tree with just the information returned in a + * QCBORItem. + */ + if(DecodeNesting_IsAtEndOfBoundedLevel(&(pMe->nesting))) { + /* At end of a bounded map/array; uNextNestLevel 0 to indicate this */ + pDecodedItem->uNextNestLevel = 0; + } else { + pDecodedItem->uNextNestLevel = DecodeNesting_GetCurrentLevel(&(pMe->nesting)); } - pMe->uTagNumberCheckOffset = uOffset; +Done: return uReturn; -#else - (void)pMe; - (void)szLabel; - (void)puTagNumber; - return QCBOR_ERR_LABEL_NOT_FOUND; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ } -#endif /* ! QCBOR_DISABLE_TAGS */ /** - * @brief Search for a map/array by label and enter it + * @brief Decode tag content for select tags (decoding layer 1). * - * @param[in] pMe The decode context. - * @param[in] pSearch The map/array to search for. + * @param[in] pMe The decode context. + * @param[out] pDecodedItem The decoded item. * - * @c pSearch is expected to contain one item of type map or array - * with the label specified. The current bounded map will be searched for - * this and if found will be entered. + * @return Decoding error code. * - * If the label is not found, or the item found is not a map or array, - * the error state is set. + * CBOR tag numbers for the item were decoded in GetNext_TaggedItem(), + * but the whole tag was not decoded. Here, the whole tags (tag number + * and tag content) are decoded. This is a + * quick pass through for items that are not tags. */ -static void -QCBORDecode_Private_SearchAndEnter(QCBORDecodeContext *pMe, QCBORItem pSearch[]) +QCBORError +QCBORDecode_Private_GetNextTagContent(QCBORDecodeContext *pMe, + QCBORItem *pDecodedItem) { - QCBORError uErr; - MapSearchInfo SearchInfo; - - // The first item in pSearch is the one that is to be - // entered. It should be the only one filled in. Any other - // will be ignored unless it causes an error. - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } - - uErr = QCBORDecode_Private_MapSearch(pMe, pSearch, &SearchInfo, NULL); + QCBORError uErr; - pMe->uLastError = (uint8_t)QCBORDecode_Private_GetItemChecks(pMe, uErr, SearchInfo.uStartOffset, pSearch); + uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem, NULL); - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } +#ifndef QCBOR_DISABLE_TAGS + uint64_t uTagNumber; + int nTagIndex; + const struct QCBORTagDecoderEntry *pTagDecoder; - if(pSearch->uDataType == QCBOR_TYPE_NONE) { - pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; - return; + if(uErr != QCBOR_SUCCESS) { + goto Done; } + /* Loop over tag numbers in reverse, those closest to content first */ + for(nTagIndex = QCBOR_MAX_TAGS_PER_ITEM-1; nTagIndex >= 0; nTagIndex--) { - /* The map or array was found. Now enter it. - * - * QCBORDecode_EnterBoundedMapOrArray() used here, requires the - * next item for the pre-order traversal cursor to be the map/array - * found by MapSearch(). The next few lines of code force the - * cursor to that. - * - * There is no need to retain the old cursor because - * QCBORDecode_EnterBoundedMapOrArray() will set it to the - * beginning of the map/array being entered. - * - * The cursor is forced by: 1) setting the input buffer position to - * the item offset found by MapSearch(), 2) setting the map/array - * counter to the total in the map/array, 3) setting the nesting - * level. Setting the map/array counter to the total is not - * strictly correct, but this is OK because this cursor only needs - * to be used to get one item and MapSearch() has already found it - * confirming it exists. - */ - UsefulInputBuf_Seek(&(pMe->InBuf), SearchInfo.uStartOffset); - - DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting)); - - DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting)); - - QCBORDecode_Private_EnterBoundedMapOrArray(pMe, pSearch->uDataType, NULL); -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_EnterMapFromMapN(QCBORDecodeContext *pMe, int64_t nLabel) -{ - 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; - - /* The map to enter was found, now finish off entering it. */ - QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach); -} - - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel) -{ -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - 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; + if(pDecodedItem->auTagNumbers[nTagIndex] == CBOR_TAG_INVALID16) { + continue; /* Empty slot, skip to next */ + } - QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach); -#else - (void)szLabel; - pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ -} + /* See if there's a content decoder for it */ + uTagNumber = QCBORDecode_Private_UnMapTagNumber(pMe, pDecodedItem->auTagNumbers[nTagIndex]); + pTagDecoder = QCBORDecode_Private_LookUpTagDecoder(pMe->pTagDecoderTable, uTagNumber); + if(pTagDecoder == NULL) { + break; /* Successful exist -- a tag that we can't decode */ + } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_EnterArrayFromMapN(QCBORDecodeContext *pMe, int64_t nLabel) -{ - 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; + /* Call the content decoder */ + uErr = pTagDecoder->pfContentDecoder(pMe, pMe->pTagDecodersContext, pTagDecoder->uTagNumber, pDecodedItem); + if(uErr != QCBOR_SUCCESS) { + break; /* Error exit from the loop */ + } - QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach); -} + /* Remove tag number from list since its content was decoded */ + pDecodedItem->auTagNumbers[nTagIndex] = CBOR_TAG_INVALID16; + } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel) -{ -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - 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; +Done: +#endif /* ! QCBOR_DISABLE_TAGS */ - QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach); -#else - (void)szLabel; - pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + return uErr; } /** - * @brief Semi-private to do the the work for EnterMap() and EnterArray(). + * @brief Consume an entire map or array including its contents. * - * @param[in] pMe The decode context - * @param[in] uType QCBOR_TYPE_MAP or QCBOR_TYPE_ARRAY. - * @param[out] pItem The data item for the map or array entered. + * @param[in] pMe The decoder context. + * @param[in] pItemToConsume The array/map whose contents are to be + * consumed. + * @param[out] puNextNestLevel The next nesting level after the item was + * fully consumed. * - * The next item in the traversal must be a map or array. This - * consumes that item and does the book keeping to enter the map or - * array. + * This may be called when @c pItemToConsume is not an array or + * map. In that case, this is just a pass through for @c puNextNestLevel + * since there is nothing to do. */ -void -QCBORDecode_Private_EnterBoundedMapOrArray(QCBORDecodeContext *pMe, - const uint8_t uType, - QCBORItem *pItem) +QCBORError +QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe, + const QCBORItem *pItemToConsume, + bool *pbBreak, + uint8_t *puNextNestLevel) { - QCBORError uErr; - - /* Must only be called on maps and arrays. */ - if(pMe->uLastError != QCBOR_SUCCESS) { - // Already in error state; do nothing. - return; - } - - /* Get the data item that is the map or array being entered. */ - QCBORItem Item; - uErr = QCBORDecode_GetNext(pMe, &Item); - if(uErr != QCBOR_SUCCESS) { - goto Done; - } - - uint8_t uItemDataType = Item.uDataType; - -#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS - if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY ) { - uItemDataType = QCBOR_TYPE_ARRAY; - } -#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + QCBORError uReturn; + QCBORItem Item; - if(uItemDataType != uType) { - uErr = QCBOR_ERR_UNEXPECTED_TYPE; - goto Done; - } + /* If it is a map or array, this will tell if it is empty. */ + const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel); - QCBORDecode_Private_SaveTagNumbers(pMe, &Item); + if(QCBORItem_IsMapOrArray(*pItemToConsume) && !bIsEmpty) { + /* There is only real work to do for non-empty maps and arrays */ + /* This works for definite- and indefinite-length maps and + * arrays by using the nesting level + */ + do { + uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, pbBreak, &Item, NULL); + if(QCBORDecode_IsUnrecoverableError(uReturn) || + uReturn == QCBOR_ERR_NO_MORE_ITEMS) { + goto Done; + } + } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel); - const bool bIsEmpty = (Item.uNextNestLevel <= Item.uNestingLevel); - if(bIsEmpty) { - if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) { - // Undo decrement done by QCBORDecode_GetNext() so the the - // the decrement when exiting the map/array works correctly - pMe->nesting.pCurrent->u.ma.uCountCursor++; - } - // Special case to increment nesting level for zero-length maps - // and arrays entered in bounded mode. - DecodeNesting_Descend(&(pMe->nesting), uType); - } + *puNextNestLevel = Item.uNextNestLevel; - pMe->uMapEndOffsetCache = QCBOR_MAP_OFFSET_CACHE_INVALID; + uReturn = QCBOR_SUCCESS; - uErr = DecodeNesting_EnterBoundedMapOrArray(&(pMe->nesting), bIsEmpty, - UsefulInputBuf_Tell(&(pMe->InBuf))); + } else { + /* pItemToConsume is not a map or array. Just pass the nesting + * level through. */ + *puNextNestLevel = pItemToConsume->uNextNestLevel; - if(pItem != NULL) { - *pItem = Item; + uReturn = QCBOR_SUCCESS; } Done: - pMe->uLastError = (uint8_t)uErr; + return uReturn; } -/** - * @brief Exit a bounded map, array or bstr (semi-private). - * - * @param[in] pMe Decode context. - * @param[in] uEndOffset The input buffer offset of the end of item exited. - * - * @returns QCBOR_SUCCESS or an error code. - * - * This is the common work for exiting a level that is a bounded map, - * array or bstr wrapped CBOR. - * - * One chunk of work is to set up the pre-order traversal so it is at - * the item just after the bounded map, array or bstr that is being - * exited. This is somewhat complex. - * - * The other work is to level-up the bounded mode to next higest - * bounded mode or the top level if there isn't one. +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE +/* + * This consumes the next item. It returns the starting position of + * the label and the length of the label. It also returns the nest + * level of the item consumed. */ static QCBORError -QCBORDecode_Private_ExitBoundedLevel(QCBORDecodeContext *pMe, - const uint32_t uEndOffset) +QCBORDecode_Private_GetLabelAndConsume(QCBORDecodeContext *pMe, + uint8_t *puNestLevel, + size_t *puLabelStart, + size_t *puLabelLen) { QCBORError uErr; + QCBORItem Item; + uint8_t uLevel; + uint32_t uLabelOffset; - /* - * First the pre-order-traversal byte offset is positioned to the - * item just after the bounded mode item that was just consumed. - */ - UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset); - - /* - * Next, set the current nesting level to one above the bounded - * level that was just exited. - * - * DecodeNesting_CheckBoundedType() is always called before this - * and makes sure pCurrentBounded is valid. - */ - DecodeNesting_LevelUpCurrent(&(pMe->nesting)); + /* Get the label and consume it should it be complex */ + *puLabelStart = UsefulInputBuf_Tell(&(pMe->InBuf)); - /* - * This does the complex work of leveling up the pre-order - * traversal when the end of a map or array or another bounded - * level is reached. It may do nothing, or ascend all the way to - * the top level. - */ - uErr = QCBORDecode_Private_NestLevelAscender(pMe, NULL, false); + uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, &Item, &uLabelOffset); if(uErr != QCBOR_SUCCESS) { goto Done; } - - /* - * This makes the next highest bounded level the current bounded - * level. If there is no next highest level, then no bounded mode - * is in effect. - */ - DecodeNesting_LevelUpBounded(&(pMe->nesting)); - - pMe->uMapEndOffsetCache = QCBOR_MAP_OFFSET_CACHE_INVALID; + *puLabelLen = uLabelOffset - *puLabelStart; + *puNestLevel = Item.uNestingLevel; + uErr = QCBORDecode_Private_ConsumeItem(pMe, &Item, NULL, &uLevel); Done: return uErr; } -/** - * @brief Get started exiting a map or array (semi-private) - * - * @param[in] pMe The decode context - * @param[in] uType QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP +/* Loop over items in a map until the end of the map looking for + * duplicates. This starts at the current position in the map, not at + * the beginning of the map. * - * This does some work for map and array exiting (but not - * bstr exiting). Then QCBORDecode_Private_ExitBoundedLevel() - * is called to do the rest. + * This saves and restores the traversal cursor and nest tracking so + * they are the same on exit as they were on entry. */ -void -QCBORDecode_Private_ExitBoundedMapOrArray(QCBORDecodeContext *pMe, - const uint8_t uType) +static QCBORError +QCBORDecode_Private_CheckDups(QCBORDecodeContext *pMe, + const uint8_t uNestLevel, + const size_t uCompareLabelStart, + const size_t uCompareLabelLen) { - if(pMe->uLastError != QCBOR_SUCCESS) { - /* Already in error state; do nothing. */ - return; - } - QCBORError uErr; + size_t uLabelStart; + size_t uLabelLen; + uint8_t uLevel; + int nCompare; - if(!DecodeNesting_IsBoundedType(&(pMe->nesting), uType)) { - uErr = QCBOR_ERR_EXIT_MISMATCH; - goto Done; - } + const QCBORDecodeNesting SaveNesting = pMe->nesting; + const UsefulInputBuf Save = pMe->InBuf; - /* - Have to set the offset to the end of the map/array - that is being exited. If there is no cached value, - from previous map search, then do a dummy search. - */ - if(pMe->uMapEndOffsetCache == QCBOR_MAP_OFFSET_CACHE_INVALID) { - QCBORItem Dummy; - Dummy.uLabelType = QCBOR_TYPE_NONE; - uErr = QCBORDecode_Private_MapSearch(pMe, &Dummy, NULL, NULL); + do { + uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uLevel, &uLabelStart, &uLabelLen); if(uErr != QCBOR_SUCCESS) { - goto Done; + if(uErr == QCBOR_ERR_NO_MORE_ITEMS) { + uErr = QCBOR_SUCCESS; /* Successful end */ + } + break; } - } - uErr = QCBORDecode_Private_ExitBoundedLevel(pMe, pMe->uMapEndOffsetCache); + if(uLevel != uNestLevel) { + break; /* Successful end of loop */ + } -Done: - pMe->uLastError = (uint8_t)uErr; -} + /* This check for dups works for labels that are preferred + * serialization and are not maps. If the labels are not in + * preferred serialization, then the check has to be more + * complicated and is type-specific because it uses the decoded + * value, not the encoded CBOR. It is further complicated for + * maps because the order of items in a map that is a label + * doesn't matter when checking that is is the duplicate of + * another map that is a label. QCBOR so far only turns on this + * dup checking as part of CDE checking which requires preferred + * serialization. See 5.6 in RFC 8949. + */ + nCompare = UsefulInputBuf_Compare(&(pMe->InBuf), + uCompareLabelStart, uCompareLabelLen, + uLabelStart, uLabelLen); + if(nCompare == 0) { + uErr = QCBOR_ERR_DUPLICATE_LABEL; + break; + } + } while (1); + + pMe->nesting = SaveNesting; + pMe->InBuf = Save; + return uErr; +} -// TODO: re order this file with tags stuff last. bstr is a tag thing -static QCBORError -QCBORDecode_Private_CheckTagNType(QCBORDecodeContext *pMe, - const QCBORItem *pItem, - const size_t uOffset, - const uint8_t *uQCBORTypes, - const uint64_t *uTagNumbers, - const uint8_t uTagRequirement, - bool *bTypeMatched); -/** - * @brief The main work of entering some byte-string wrapped CBOR. - * - * @param[in] pMe The decode context. - * @param[in] pItem The byte string item. - * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX - * @param[out] pBstr Pointer and length of byte string entered. - * - * This is called once the byte string item has been decoded to do all - * the book keeping work for descending a nesting level into the - * nested CBOR. - * - * See QCBORDecode_EnterBstrWrapped() for details on uTagRequirement. +/* This does sort order and duplicate detection on a map. The and all + * it's members must be in preferred serialization so the comparisons + * work correctly. */ static QCBORError -QCBORDecode_Private_EnterBstrWrapped(QCBORDecodeContext *pMe, - const QCBORItem *pItem, - const uint8_t uTagRequirement, - const size_t uOffset, - UsefulBufC *pBstr) +QCBORDecode_Private_CheckMap(QCBORDecodeContext *pMe, const QCBORItem *pMapToCheck) { - bool bTypeMatched; - QCBORError uError; - - const uint8_t uTypes[] = {QBCOR_TYPE_WRAPPED_CBOR, QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE, QCBOR_TYPE_NONE}; - const uint64_t uTagNumbers[] = {CBOR_TAG_CBOR, CBOR_TAG_CBOR_SEQUENCE, CBOR_TAG_INVALID64}; - + QCBORError uErr; + uint8_t uNestLevel; + size_t offset2, offset1, length2, length1; - if(pBstr) { - *pBstr = NULLUsefulBufC; - } + const QCBORDecodeNesting SaveNesting = pMe->nesting; + const UsefulInputBuf Save = pMe->InBuf; + pMe->bAllowAllLabels = 1; - if(pMe->uLastError != QCBOR_SUCCESS) { - return pMe->uLastError; - } + /* This loop runs over all the items in the map once, comparing + * each adjacent pair for correct ordering. It also calls CheckDup + * on each one which also runs over the remaining items in the map + * checking for duplicates. So duplicate checking runs in n^2. + */ - if(pItem->uDataAlloc) { - return QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING; - } + offset2 = SIZE_MAX; + length2 = SIZE_MAX; // To avoid uninitialized warning + while(1) { + uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uNestLevel, &offset1, &length1); + if(uErr != QCBOR_SUCCESS) { + if(uErr == QCBOR_ERR_NO_MORE_ITEMS) { + uErr = QCBOR_SUCCESS; /* Successful exit from loop */ + } + break; + } - uError = QCBORDecode_Private_CheckTagNType(pMe, - pItem, - uOffset, - uTypes, // TODO: maybe this should be empty - uTagNumbers, - uTagRequirement, - &bTypeMatched); + if(uNestLevel < pMapToCheck->uNextNestLevel) { + break; /* Successful exit from loop */ + } - if(pItem->uDataType != QCBOR_TYPE_BYTE_STRING) { - uError = QCBOR_ERR_BAD_TAG_CONTENT; // TODO: error - } + if(offset2 != SIZE_MAX) { + /* Check that the labels are ordered. Check is not done the + * first time through the loop when offset2 is unset. Since + * this does comparison of the items in encoded form they + * must be preferred serialization encoded. See RFC 8949 + * 4.2.1. + */ + if(UsefulInputBuf_Compare(&(pMe->InBuf), offset2, length2, offset1, length1) > 0) { + uErr = QCBOR_ERR_UNSORTED; + break; + } + } + uErr = QCBORDecode_Private_CheckDups(pMe, pMapToCheck->uNextNestLevel, offset1, length1); + if(uErr != QCBOR_SUCCESS) { + break; + } - if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) { - /* Reverse the decrement done by GetNext() for the bstr so the - * increment in QCBORDecode_NestLevelAscender() called by - * ExitBoundedLevel() will work right. - */ - DecodeNesting_ReverseDecrement(&(pMe->nesting)); + offset2 = offset1; + length2 = length1; } - if(pBstr) { - *pBstr = pItem->val.string; - } + pMe->bAllowAllLabels = 0; + pMe->nesting = SaveNesting; + pMe->InBuf = Save; - /* This saves the current length of the UsefulInputBuf and then - * narrows the UsefulInputBuf to start and length of the wrapped - * CBOR that is being entered. - * - * Most of these calls are simple inline accessors so this doesn't - * amount to much code. - */ + return uErr; +} +#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */ - const size_t uPreviousLength = UsefulInputBuf_GetBufferLength(&(pMe->InBuf)); - /* This check makes the cast of uPreviousLength to uint32_t below safe. */ - if(uPreviousLength >= QCBOR_MAX_DECODE_INPUT_SIZE) { - uError = QCBOR_ERR_INPUT_TOO_LARGE; - goto Done; - } +QCBORError +QCBORDecode_Private_GetItemChecks(QCBORDecodeContext *pMe, + QCBORError uErr, + const size_t uOffset, + QCBORItem *pDecodedItem) +{ + (void)pMe; /* Avoid warning for next two ifdefs */ + (void)uOffset; - const size_t uStartOfBstr = UsefulInputBuf_PointerToOffset(&(pMe->InBuf), - pItem->val.string.ptr); - /* This check makes the cast of uStartOfBstr to uint32_t below safe. */ - if(uStartOfBstr == SIZE_MAX || uStartOfBstr > QCBOR_MAX_DECODE_INPUT_SIZE) { - /* This should never happen because pItem->val.string.ptr should - * always be valid since it was just returned. - */ - uError = QCBOR_ERR_INPUT_TOO_LARGE; - goto Done; +#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE + if(uErr == QCBOR_SUCCESS && + pMe->uDecodeMode & QCBOR_DECODE_ONLY_SORTED_MAPS && + pDecodedItem->uDataType == QCBOR_TYPE_MAP) { + /* Traverse map checking sort order and for duplicates */ + uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem); } +#endif /* ! QCBOR_DISABLE_CONFORMANCE */ - const size_t uEndOfBstr = uStartOfBstr + pItem->val.string.len; - - UsefulInputBuf_Seek(&(pMe->InBuf), uStartOfBstr); - UsefulInputBuf_SetBufferLength(&(pMe->InBuf), uEndOfBstr); - - uError = DecodeNesting_DescendIntoBstrWrapped(&(pMe->nesting), - (uint32_t)uPreviousLength, - (uint32_t)uStartOfBstr); -Done: - return uError; -} - - -static void -QCBORDecode_Private_GetAndTell(QCBORDecodeContext *pMe, QCBORItem *Item, size_t *uOffset) -{ #ifndef QCBOR_DISABLE_TAGS - if(pMe->uLastError != QCBOR_SUCCESS) { - return; + if(uErr == QCBOR_SUCCESS && + !(pMe->uDecodeMode & QCBOR_DECODE_ALLOW_UNPROCESSED_TAG_NUMBERS) && + pDecodedItem->auTagNumbers[0] != CBOR_TAG_INVALID16) { + /* Not QCBOR v1; there are tag numbers -- check they were consumed */ + if(uOffset != pMe->uTagNumberCheckOffset || pMe->uTagNumberIndex != 255) { + uErr = QCBOR_ERR_UNPROCESSED_TAG_NUMBER; + } } +#endif /* ! QCBOR_DISABLE_TAGS */ - *uOffset = QCBORDecode_Tell(pMe); -#else - *uOffset = SIZE_MAX; + if(uErr != QCBOR_SUCCESS) { + pDecodedItem->uDataType = QCBOR_TYPE_NONE; + pDecodedItem->uLabelType = QCBOR_TYPE_NONE; + } -#endif /* ! QCBOR_DISABLE_TAGS */ - pMe->uLastError = (uint8_t)QCBORDecode_Private_GetNextTagContent(pMe, Item); + return uErr; } /* * Public function, see header qcbor/qcbor_decode.h file */ -void -QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pBstr) +QCBORError +QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) { - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); - pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe, - &Item, - uTagRequirement, - uOffset, - pBstr); + QCBORError uErr; + size_t uOffset; + + uOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); + uErr = QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem); + uErr = QCBORDecode_Private_GetItemChecks(pMe, uErr, uOffset, pDecodedItem); + return uErr; } /* * Public function, see header qcbor/qcbor_decode.h file */ -void -QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pBstr) +QCBORError +QCBORDecode_PeekNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) { - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_BYTE_STRING, &Item, &uOffset); - pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe, - &Item, - uTagRequirement, - uOffset, - pBstr); + const QCBORDecodeNesting SaveNesting = pMe->nesting; + const UsefulInputBuf Save = pMe->InBuf; + + QCBORError uErr = QCBORDecode_GetNext(pMe, pDecodedItem); + + pMe->nesting = SaveNesting; + pMe->InBuf = Save; + + return uErr; } @@ -4557,20 +2276,15 @@ QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pMe, * Public function, see header qcbor/qcbor_decode.h file */ void -QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pBstr) +QCBORDecode_VPeekNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) { - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_BYTE_STRING, &Item, &uOffset); - pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe, - &Item, - uTagRequirement, - uOffset, - pBstr); + if(pMe->uLastError != QCBOR_SUCCESS) { + pDecodedItem->uDataType = QCBOR_TYPE_NONE; + pDecodedItem->uLabelType = QCBOR_TYPE_NONE; + return; + } + + pMe->uLastError = (uint8_t)QCBORDecode_PeekNext(pMe, pDecodedItem); } @@ -4578,93 +2292,65 @@ QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pMe, * Public function, see header qcbor/qcbor_decode.h file */ void -QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pMe) +QCBORDecode_VGetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) { if(pMe->uLastError != QCBOR_SUCCESS) { - // Already in error state; do nothing. - return; - } - - if(!DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_BYTE_STRING)) { - pMe->uLastError = QCBOR_ERR_EXIT_MISMATCH; + pDecodedItem->uDataType = QCBOR_TYPE_NONE; + pDecodedItem->uLabelType = QCBOR_TYPE_NONE; return; } - const uint32_t uEndOfBstr = (uint32_t)UsefulInputBuf_GetBufferLength(&(pMe->InBuf)); - - /* - Reset the length of the UsefulInputBuf to what it was before - the bstr wrapped CBOR was entered. - */ - UsefulInputBuf_SetBufferLength(&(pMe->InBuf), - DecodeNesting_GetPreviousBoundedEnd(&(pMe->nesting))); - - - QCBORError uErr = QCBORDecode_Private_ExitBoundedLevel(pMe, uEndOfBstr); - pMe->uLastError = (uint8_t)uErr; + pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, pDecodedItem); + QCBORDecode_Private_SaveTagNumbers(pMe, pDecodedItem); } - -/** - * @brief Process simple type true and false, a boolean - * - * @param[in] pMe The decode context. - * @param[in] pItem The item with either true or false. - * @param[out] pBool The boolean value output. - * - * Sets the internal error if the item isn't a true or a false. Also - * records any tag numbers as the tag numbers of the last item. +/* + * Public function, see header qcbor/qcbor_decode.h file */ -static void -QCBORDecode_Private_ProcessBool(QCBORDecodeContext *pMe, - const QCBORItem *pItem, - bool *pBool) +QCBORError +QCBORDecode_PartialFinish(QCBORDecodeContext *pMe, size_t *puConsumed) { - if(pMe->uLastError != QCBOR_SUCCESS) { - /* Already in error state, do nothing */ - return; + if(puConsumed != NULL) { + *puConsumed = pMe->InBuf.cursor; } - switch(pItem->uDataType) { - case QCBOR_TYPE_TRUE: - *pBool = true; - break; + QCBORError uReturn = pMe->uLastError; - case QCBOR_TYPE_FALSE: - *pBool = false; - break; + if(uReturn != QCBOR_SUCCESS) { + goto Done; + } - default: - pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; - break; + /* Error out if all the maps/arrays are not closed out */ + if(!DecodeNesting_IsCurrentAtTop(&(pMe->nesting))) { + uReturn = QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED; + goto Done; } -} + /* Error out if not all the bytes are consumed */ + if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf))) { + uReturn = QCBOR_ERR_EXTRA_BYTES; + } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetBool(QCBORDecodeContext *pMe, bool *pValue) -{ - QCBORItem Item; - QCBORDecode_VGetNext(pMe, &Item); - QCBORDecode_Private_ProcessBool(pMe, &Item, pValue); +Done: + return uReturn; } /* * Public function, see header qcbor/qcbor_decode.h file */ -void -QCBORDecode_GetBoolInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - bool *pValue) +QCBORError +QCBORDecode_Finish(QCBORDecodeContext *pMe) { - QCBORItem Item; - QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item); - QCBORDecode_Private_ProcessBool(pMe, &Item, pValue); +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS + /* Call the destructor for the string allocator if there is one. + * Always called, even if there are errors; always have to clean up. + */ + StringAllocator_Destruct(&(pMe->StringAllocator)); +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ + + return QCBORDecode_PartialFinish(pMe, NULL); } @@ -4672,580 +2358,333 @@ QCBORDecode_GetBoolInMapN(QCBORDecodeContext *pMe, * Public function, see header qcbor/qcbor_decode.h file */ void -QCBORDecode_GetBoolInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - bool *pValue) -{ - QCBORItem Item; - QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item); - QCBORDecode_Private_ProcessBool(pMe, &Item, pValue); -} - - -/** - * @brief Process simple values. - * - * @param[in] pMe The decode context. - * @param[in] pItem The item with the simple value. - * @param[out] puSimple The simple value output. - * - * Sets the internal error if the item isn't a true or a false. Also - * records any tag numbers as the tag numbers of the last item. - */ -static void -QCBORDecode_Private_ProcessSimple(QCBORDecodeContext *pMe, - const QCBORItem *pItem, - uint8_t *puSimple) +QCBORDecode_VGetNextConsume(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem) { - if(pMe->uLastError != QCBOR_SUCCESS) { - return; - } - - /* It's kind of lame to remap true...undef back to simple values, but - * this function isn't used much and to not do it would require - * changing GetNext() behavior in an incompatible way. - */ - switch(pItem->uDataType) { - case QCBOR_TYPE_UKNOWN_SIMPLE: - *puSimple = pItem->val.uSimple; - break; - - case QCBOR_TYPE_TRUE: - *puSimple = CBOR_SIMPLEV_TRUE; - break; - - case QCBOR_TYPE_FALSE: - *puSimple = CBOR_SIMPLEV_FALSE; - break; - - case QCBOR_TYPE_NULL: - *puSimple = CBOR_SIMPLEV_NULL; - break; - - case QCBOR_TYPE_UNDEF: - *puSimple = CBOR_SIMPLEV_UNDEF; - break; + QCBORDecode_VGetNext(pMe, pDecodedItem); - default: - pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; - return; + if(pMe->uLastError == QCBOR_SUCCESS) { + pMe->uLastError = (uint8_t)QCBORDecode_Private_ConsumeItem(pMe, pDecodedItem, NULL, + &pDecodedItem->uNextNestLevel); } } -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetSimple(QCBORDecodeContext *pMe, uint8_t *puSimple) -{ - QCBORItem Item; - QCBORDecode_VGetNext(pMe, &Item); - QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimple); -} - -/* - * Public function, see header qcbor/qcbor_decode.h file - */ -void -QCBORDecode_GetSimpleInMapN(QCBORDecodeContext *pMe, - int64_t nLabel, - uint8_t *puSimpleValue) -{ - QCBORItem Item; - QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item); - QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimpleValue); -} /* * Public function, see header qcbor/qcbor_decode.h file */ -void -QCBORDecode_GetSimpleInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - uint8_t *puSimpleValue) -{ - QCBORItem Item; - QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item); - QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimpleValue); -} - - - - -#ifndef QCBOR_DISABLE_TAGS -// TODO: uTagNumber might be better a list than calling this multiple times -static QCBORError -QCBORDecode_Private_Check1TagNumber(const QCBORDecodeContext *pMe, - const QCBORItem *pItem, - const uint64_t uTagNumber, - const size_t uOffset) +QCBORError +QCBORDecode_EndCheck(QCBORDecodeContext *pMe) { - if(pItem->auTagNumbers[0] == CBOR_TAG_INVALID16) { - /* There are no tag numbers at all, so no unprocessed */ - return QCBOR_SUCCESS; - } - - /* There are some tag numbers, so keep checking. This check passes - * if there is one and only one tag number that matches uTagNumber - */ - - // TODO: behave different in v1 and v2? - - const uint64_t uInnerTag = QCBORDecode_GetNthTagNumber(pMe, pItem, 0); + size_t uCursorOffset; + QCBORError uErr; - if(uInnerTag == uTagNumber && pItem->auTagNumbers[1] == CBOR_TAG_INVALID16 ) { - /* The only tag number is the one we are processing so no unprocessed */ - return QCBOR_SUCCESS; + uErr = QCBORDecode_GetError(pMe); + if(uErr != QCBOR_SUCCESS) { + return uErr; } - if(uOffset != pMe->uTagNumberCheckOffset) { - /* processed tag numbers are for some other item, not us */ - return QCBOR_ERR_UNPROCESSED_TAG_NUMBER; - } + uCursorOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); - if(pMe->uTagNumberIndex != 1) { - return QCBOR_ERR_UNPROCESSED_TAG_NUMBER; + if(uCursorOffset == UsefulInputBuf_GetBufferLength(&(pMe->InBuf))) { + return QCBOR_ERR_NO_MORE_ITEMS; } return QCBOR_SUCCESS; } -#endif -static QCBORError -QCBORDecode_Private_CheckTagNType(QCBORDecodeContext *pMe, - const QCBORItem *pItem, - const size_t uOffset, - const uint8_t *uQCBORTypes, - const uint64_t *uTagNumbers, - const uint8_t uTagRequirement, - bool *bTypeMatched) +/** + * @brief Semi-private. Get pointer, length and item for an array or map. + * + * @param[in] pMe The decode context. + * @param[in] uType CBOR major type, either array/map. + * @param[out] pItem The item for the array/map. + * @param[out] pEncodedCBOR Pointer and length of the encoded map or array. + * + * The next item to be decoded must be a map or array as specified by @c uType. + * + * @c pItem will be filled in with the label and tags of the array or map + * in addition to @c pEncodedCBOR giving the pointer and length of the + * encoded CBOR. + * + * When this is complete, the traversal cursor is at the end of the array or + * map that was retrieved. + */ +void +QCBORDecode_Private_GetArrayOrMap(QCBORDecodeContext *pMe, + const uint8_t uType, + QCBORItem *pItem, + UsefulBufC *pEncodedCBOR) { - const int nTagReq = uTagRequirement & ~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS; - - *bTypeMatched = false; - for(const uint8_t *pTNum = uQCBORTypes; *pTNum != QCBOR_TYPE_NONE; pTNum++) { - if(pItem->uDataType == *pTNum) { - *bTypeMatched = true; - break; - } - } - -#ifndef QCBOR_DISABLE_TAGS - bool bTagNumberMatched; - QCBORError uErr; - const uint64_t uInnerTag = QCBORDecode_GetNthTagNumber(pMe, pItem, 0); - - bTagNumberMatched = false; - for(const uint64_t *pQType = uTagNumbers; *pQType != CBOR_TAG_INVALID64; pQType++) { - if(uInnerTag == *pQType) { - bTagNumberMatched = true; - break; - } - } - - - if(nTagReq == QCBOR_TAG_REQUIREMENT_TAG) { - /* There must be a tag number */ - if(!bTagNumberMatched && !*bTypeMatched) { - return QCBOR_ERR_UNEXPECTED_TYPE; // TODO: error code - } - - } else if(nTagReq == QCBOR_TAG_REQUIREMENT_NOT_A_TAG) { - if(bTagNumberMatched || *bTypeMatched) { - return QCBOR_ERR_UNEXPECTED_TYPE; // TODO: error code - } + QCBORError uErr; + uint8_t uNestLevel; + size_t uStartingCursor; + size_t uStartOfReturned; + size_t uEndOfReturned; + size_t uTempSaveCursor; + bool bInMap; + QCBORItem LabelItem; + bool EndedByBreak; - } else if(nTagReq == QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG) { - /* No check necessary */ - } + uStartingCursor = UsefulInputBuf_Tell(&(pMe->InBuf)); + bInMap = DecodeNesting_IsCurrentTypeMap(&(pMe->nesting)); - /* Now check if there are extra tags and if there's an error in them */ - if(!(uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS)) { - /* The flag to ignore extra is not set, so keep checking */ - for(const uint64_t *pTNum = uTagNumbers; *pTNum != CBOR_TAG_INVALID64; pTNum++) { - uErr = QCBORDecode_Private_Check1TagNumber(pMe, pItem, *pTNum, uOffset); - if(uErr != QCBOR_SUCCESS) { - return uErr; - } - } + /* Could call GetNext here, but don't need to because this + * is only interested in arrays and maps. TODO: switch to GetNext()? */ + uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem, NULL); + if(uErr != QCBOR_SUCCESS) { + pMe->uLastError = (uint8_t)uErr; + return; } - return QCBOR_SUCCESS; -#else - (void)pMe; - (void)uOffset; - (void)uTagNumbers; - - if(nTagReq != QCBOR_TAG_REQUIREMENT_TAG && bTypeMatched) { - return QCBOR_SUCCESS; - } else { - return QCBOR_ERR_UNEXPECTED_TYPE; + uint8_t uItemDataType = pItem->uDataType; +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) { + uItemDataType = QCBOR_TYPE_ARRAY; } +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ -#endif - -} + if(uItemDataType != uType) { + pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; + return; + } + if(bInMap) { + /* If the item is in a map, the start of the array/map + * itself, not the label, must be found. Do this by + * rewinding to the starting position and fetching + * just the label data item. QCBORDecode_Private_GetNextTagNumber() + * doesn't do any of the array/map item counting or nesting + * level tracking. Used here it will just fetech the label + * data item. + * + * Have to save the cursor and put it back to the position + * after the full item once the label as been fetched by + * itself. + */ + uTempSaveCursor = UsefulInputBuf_Tell(&(pMe->InBuf)); + UsefulInputBuf_Seek(&(pMe->InBuf), uStartingCursor); -void -QCBORDecode_Private_ProcessTagItemMulti(QCBORDecodeContext *pMe, - QCBORItem *pItem, - const uint8_t uTagRequirement, - const uint8_t uQCBORTypes[], - const uint64_t uTagNumbers[], - QCBORTagContentCallBack *pfCB, - size_t uOffset) -{ - QCBORError uErr; - bool bTypeMatched; + /* Item has been fetched once so safe to ignore error */ + (void)QCBORDecode_Private_GetNextTagNumber(pMe, &LabelItem); - if(pMe->uLastError != QCBOR_SUCCESS) { - return; + uStartOfReturned = UsefulInputBuf_Tell(&(pMe->InBuf)); + UsefulInputBuf_Seek(&(pMe->InBuf), uTempSaveCursor); + } else { + uStartOfReturned = uStartingCursor; } - uErr = QCBORDecode_Private_CheckTagNType(pMe, - pItem, - uOffset, - uQCBORTypes, - uTagNumbers, - uTagRequirement, - &bTypeMatched); + /* Consume the entire array/map to find the end */ + uErr = QCBORDecode_Private_ConsumeItem(pMe, pItem, &EndedByBreak, &uNestLevel); if(uErr != QCBOR_SUCCESS) { + pMe->uLastError = (uint8_t)uErr; goto Done; } - if(!bTypeMatched) { - /* Tag content wasn't previously processed, do it now */ - uErr = (*pfCB)(pMe, NULL, uTagNumbers[0], pItem); - if(uErr != QCBOR_SUCCESS) { - goto Done; - } + /* Fill in returned values */ + uEndOfReturned = UsefulInputBuf_Tell(&(pMe->InBuf)); + if(EndedByBreak) { + /* When ascending nesting levels, a break for the level above + * was consumed. That break is not a part of what is consumed here. */ + uEndOfReturned--; } + pEncodedCBOR->ptr = UsefulInputBuf_OffsetToPointer(&(pMe->InBuf), uStartOfReturned); + pEncodedCBOR->len = uEndOfReturned - uStartOfReturned; Done: - pMe->uLastError = (uint8_t)uErr; + return; } -/* - **/ -void -QCBORDecode_Private_ProcessTagItem(QCBORDecodeContext *pMe, - QCBORItem *pItem, - const uint8_t uTagRequirement, - const uint8_t uQCBORTypes[], - const uint64_t uTagNumber, - QCBORTagContentCallBack *pfCB, - size_t uOffset) -{ - uint64_t auTagNumbers[2]; - - auTagNumbers[0] = uTagNumber; - auTagNumbers[1] = CBOR_TAG_INVALID64; - - QCBORDecode_Private_ProcessTagItemMulti(pMe, - pItem, - uTagRequirement, - uQCBORTypes, - auTagNumbers, - pfCB, - uOffset); -} +#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS -static void -QCBORDecode_Private_ProcessTagOne(QCBORDecodeContext *pMe, - QCBORItem *pItem, - const uint8_t uTagRequirement, - const uint8_t uQCBORType, - const uint64_t uTagNumber, - QCBORTagContentCallBack *pfCB, - const size_t uOffset) -{ - uint8_t auQCBORType[2]; - - auQCBORType[0] = uQCBORType; - auQCBORType[1] = QCBOR_TYPE_NONE; - - QCBORDecode_Private_ProcessTagItem(pMe, - pItem, - uTagRequirement, - auQCBORType, - uTagNumber, - pfCB, - uOffset); -} +/* =========================================================================== + MemPool -- BUILT-IN SIMPLE STRING ALLOCATOR + This implements a simple sting allocator for indefinite-length + strings that can be enabled by calling QCBORDecode_SetMemPool(). It + implements the function type QCBORStringAllocate and allows easy + use of it. + This particular allocator is built-in for convenience. The caller + can implement their own. All of this following code will get + dead-stripped if QCBORDecode_SetMemPool() is not called. + This is a very primitive memory allocator. It does not track + individual allocations, only a high-water mark. A free or + reallocation must be of the last chunk allocated. -/* - * Public function, see header qcbor/qcbor_spiffy_decode.h file - */ -void -QCBORDecode_GetEpochDate(QCBORDecodeContext *pMe, - uint8_t uTagRequirement, - int64_t *pnTime) -{ - QCBORItem Item; - size_t uOffset; + The size of the pool and offset to free memory are packed into the + first 8 bytes of the memory pool so we don't have to keep them in + the decode context. Since the address of the pool may not be + aligned, they have to be packed and unpacked as if they were + serialized data of the wire or such. - QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - QCBOR_TYPE_DATE_EPOCH, - CBOR_TAG_DATE_EPOCH, - QCBORDecode_DateEpochTagCB, - uOffset); - *pnTime = Item.val.epochDate.nSeconds; -} + The sizes packed in are uint32_t to be the same on all CPU types + and simplify the code. + ========================================================================== */ -/* - * Public function, see header qcbor/qcbor_spiffy_decode.h file - */ -void -QCBORDecode_GetEpochDateInMapN(QCBORDecodeContext *pMe, - int64_t nLabel, - uint8_t uTagRequirement, - int64_t *pnTime) +static int +MemPool_Unpack(const void *pMem, uint32_t *puPoolSize, uint32_t *puFreeOffset) { - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - QCBOR_TYPE_DATE_EPOCH, - CBOR_TAG_DATE_EPOCH, - QCBORDecode_DateEpochTagCB, - uOffset); - *pnTime = Item.val.epochDate.nSeconds; -} - + // Use of UsefulInputBuf is overkill, but it is convenient. + UsefulInputBuf UIB; -/* - * Public function, see header qcbor/qcbor_spiffy_decode.h file - */ -void -QCBORDecode_GetEpochDateInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - uint8_t uTagRequirement, - int64_t *pnTime) -{ - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - QCBOR_TYPE_DATE_EPOCH, - CBOR_TAG_DATE_EPOCH, - QCBORDecode_DateEpochTagCB, - uOffset); - *pnTime = Item.val.epochDate.nSeconds; + // Just assume the size here. It was checked during SetUp so + // the assumption is safe. + UsefulInputBuf_Init(&UIB, (UsefulBufC){pMem,QCBOR_DECODE_MIN_MEM_POOL_SIZE}); + *puPoolSize = UsefulInputBuf_GetUint32(&UIB); + *puFreeOffset = UsefulInputBuf_GetUint32(&UIB); + return UsefulInputBuf_GetError(&UIB); } -/* - * Public function, see header qcbor/qcbor_decode.h - */ -void -QCBORDecode_GetEpochDays(QCBORDecodeContext *pMe, - uint8_t uTagRequirement, - int64_t *pnDays) +static int +MemPool_Pack(UsefulBuf Pool, uint32_t uFreeOffset) { - QCBORItem Item; - size_t uOffset; + // Use of UsefulOutBuf is overkill, but convenient. The + // length check performed here is useful. + UsefulOutBuf UOB; - QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - QCBOR_TYPE_DAYS_EPOCH, - CBOR_TAG_DAYS_EPOCH, - QCBORDecode_DaysEpochTagCB, - uOffset); - *pnDays = Item.val.epochDays; + UsefulOutBuf_Init(&UOB, Pool); + UsefulOutBuf_AppendUint32(&UOB, (uint32_t)Pool.len); // size of pool + UsefulOutBuf_AppendUint32(&UOB, uFreeOffset); // first free position + return UsefulOutBuf_GetError(&UOB); } /* - * Public function, see header qcbor/qcbor_decode.h - */ -void -QCBORDecode_GetEpochDaysInMapN(QCBORDecodeContext *pMe, - int64_t nLabel, - uint8_t uTagRequirement, - int64_t *pnDays) -{ - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - QCBOR_TYPE_DAYS_EPOCH, - CBOR_TAG_DAYS_EPOCH, - QCBORDecode_DaysEpochTagCB, - uOffset); - *pnDays = Item.val.epochDays; -} + Internal function for an allocation, reallocation free and destuct. + Having only one function rather than one each per mode saves space in + QCBORDecodeContext. -/* - * Public function, see header qcbor/qcbor_decode.h + Code Reviewers: THIS FUNCTION DOES POINTER MATH */ -void -QCBORDecode_GetEpochDaysInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - uint8_t uTagRequirement, - int64_t *pnDays) -{ - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - QCBOR_TYPE_DAYS_EPOCH, - CBOR_TAG_DAYS_EPOCH, - QCBORDecode_DaysEpochTagCB, - uOffset); - *pnDays = Item.val.epochDays; -} - - - - -void -QCBORDecode_Private_GetTaggedString(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - const uint8_t uQCBOR_Type, - const uint64_t uTagNumber, - UsefulBufC *pStr) +static UsefulBuf +MemPool_Function(void *pPool, void *pMem, size_t uNewSize) { - QCBORItem Item; - size_t uOffset; + UsefulBuf ReturnValue = NULLUsefulBuf; - QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); - QCBORDecode_Private_ProcessTagOne(pMe, - &Item, - uTagRequirement, - uQCBOR_Type, - uTagNumber, - QCBORDecode_StringsTagCB, - uOffset); + uint32_t uPoolSize; + uint32_t uFreeOffset; - if(pMe->uLastError == QCBOR_SUCCESS) { - *pStr = Item.val.string; - } else { - *pStr = NULLUsefulBufC; + if(uNewSize > UINT32_MAX) { + // This allocator is only good up to 4GB. This check should + // optimize out if sizeof(size_t) == sizeof(uint32_t) + goto Done; } -} - - + const uint32_t uNewSize32 = (uint32_t)uNewSize; -static void -QCBORDecode_Private_GetMIME(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - QCBORItem *pItem, - UsefulBufC *pValue, - bool *pbIsTag257, - size_t uOffset) -{ - QCBORError uErr; + if(MemPool_Unpack(pPool, &uPoolSize, &uFreeOffset)) { + goto Done; + } - const uint8_t puTypes[] = {QCBOR_TYPE_MIME, QCBOR_TYPE_BINARY_MIME, QCBOR_TYPE_NONE}; + if(uNewSize) { + if(pMem) { + // REALLOCATION MODE + // Calculate pointer to the end of the memory pool. It is + // assumed that pPool + uPoolSize won't wrap around by + // assuming the caller won't pass a pool buffer in that is + // not in legitimate memory space. + const void *pPoolEnd = (uint8_t *)pPool + uPoolSize; - const uint64_t puTNs[] = {CBOR_TAG_MIME, CBOR_TAG_BINARY_MIME, CBOR_TAG_INVALID64}; + // Check that the pointer for reallocation is in the range of the + // pool. This also makes sure that pointer math further down + // doesn't wrap under or over. + if(pMem >= pPool && pMem < pPoolEnd) { + // Offset to start of chunk for reallocation. This won't + // wrap under because of check that pMem >= pPool. Cast + // is safe because the pool is always less than UINT32_MAX + // because of check in QCBORDecode_SetMemPool(). + const uint32_t uMemOffset = (uint32_t)((uint8_t *)pMem - (uint8_t *)pPool); - QCBORDecode_Private_ProcessTagItemMulti(pMe, - pItem, - uTagRequirement, - puTypes, - puTNs, - QCBORDecode_MIMETagCB, - uOffset); - if(pMe->uLastError) { - return; - } + // Check to see if the allocation will fit. uPoolSize - + // uMemOffset will not wrap under because of check that + // pMem is in the range of the uPoolSize by check above. + if(uNewSize <= uPoolSize - uMemOffset) { + ReturnValue.ptr = pMem; + ReturnValue.len = uNewSize; - if(pItem->uDataType == QCBOR_TYPE_MIME) { - *pbIsTag257 = false; - } else if(pItem->uDataType == QCBOR_TYPE_BINARY_MIME) { - *pbIsTag257 = true; + // Addition won't wrap around over because uNewSize was + // checked to be sure it is less than the pool size. + uFreeOffset = uMemOffset + uNewSize32; + } + } + } else { + // ALLOCATION MODE + // uPoolSize - uFreeOffset will not underflow because this + // pool implementation makes sure uFreeOffset is always + // smaller than uPoolSize through this check here and + // reallocation case. + if(uNewSize <= uPoolSize - uFreeOffset) { + ReturnValue.len = uNewSize; + ReturnValue.ptr = (uint8_t *)pPool + uFreeOffset; + uFreeOffset += (uint32_t)uNewSize; + } + } + } else { + if(pMem) { + // FREE MODE + // Cast is safe because of limit on pool size in + // QCBORDecode_SetMemPool() + uFreeOffset = (uint32_t)((uint8_t *)pMem - (uint8_t *)pPool); + } else { + // DESTRUCT MODE + // Nothing to do for this allocator + } } - *pValue = pItem->val.string; - - uErr = QCBOR_SUCCESS; + UsefulBuf Pool = {pPool, uPoolSize}; + MemPool_Pack(Pool, uFreeOffset); - pMe->uLastError = (uint8_t)uErr; +Done: + return ReturnValue; } -void -QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pMe, - const uint8_t uTagRequirement, - UsefulBufC *pMessage, - bool *pbIsTag257) +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +QCBORError +QCBORDecode_SetMemPool(QCBORDecodeContext *pMe, + UsefulBuf Pool, + bool bAllStrings) { - QCBORItem Item; - size_t uOffset; - - QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); - QCBORDecode_Private_GetMIME(pMe, - uTagRequirement, - &Item, - pMessage, - pbIsTag257, - uOffset); -} + // The pool size and free mem offset are packed into the beginning + // of the pool memory. This compile time check makes sure the + // constant in the header is correct. This check should optimize + // down to nothing. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif + if(QCBOR_DECODE_MIN_MEM_POOL_SIZE < 2 * sizeof(uint32_t)) { + return QCBOR_ERR_MEM_POOL_SIZE; + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif -void -QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pMe, - const int64_t nLabel, - const uint8_t uTagRequirement, - UsefulBufC *pMessage, - bool *pbIsTag257) -{ - QCBORItem Item; - size_t uOffset; + // The pool size and free offset packed in to the beginning of pool + // memory are only 32-bits. This check will optimize out on 32-bit + // machines. + if(Pool.len > UINT32_MAX) { + return QCBOR_ERR_MEM_POOL_SIZE; + } - QCBORDecode_Private_GetItemInMapNoCheckN(pMe, nLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_GetMIME(pMe, - uTagRequirement, - &Item, - pMessage, - pbIsTag257, - uOffset); -} + // This checks that the pool buffer given is big enough. + if(MemPool_Pack(Pool, QCBOR_DECODE_MIN_MEM_POOL_SIZE)) { + return QCBOR_ERR_MEM_POOL_SIZE; + } -void -QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pMe, - const char *szLabel, - const uint8_t uTagRequirement, - UsefulBufC *pMessage, - bool *pbIsTag257) -{ - QCBORItem Item; - size_t uOffset; + QCBORDecode_SetUpAllocator(pMe, MemPool_Function, Pool.ptr, bAllStrings); - QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item, &uOffset); - QCBORDecode_Private_GetMIME(pMe, - uTagRequirement, - &Item, - pMessage, - pbIsTag257, - uOffset); + return QCBOR_SUCCESS; } +#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */ diff --git a/src/qcbor_number_decode.c b/src/qcbor_number_decode.c index 6873b94..5c6f3e3 100644 --- a/src/qcbor_number_decode.c +++ b/src/qcbor_number_decode.c @@ -1,5 +1,5 @@ /* ========================================================================== - * number_decode.c -- Number decoding beyond the basic ints and floats + * qcbor_number_decode.c -- Number decoding beyond the basic ints and floats * * Copyright (c) 2016-2018, The Linux Foundation. * Copyright (c) 2018-2024, Laurence Lundblade. @@ -10,7 +10,7 @@ * * See BSD-3-Clause license in README.md * - * Created on 11/14/24 from qcbor_decode.c + * Created on 11/14/24 from qcbor_decode.c * ========================================================================== */ diff --git a/src/qcbor_spiffy_decode.c b/src/qcbor_spiffy_decode.c new file mode 100644 index 0000000..5c25f54 --- /dev/null +++ b/src/qcbor_spiffy_decode.c @@ -0,0 +1,1197 @@ + + +/* ========================================================================== + * qcbor_spiffy_decode.c -- "Spiffy" QCBOR decoding + * + * Copyright (c) 2016-2018, The Linux Foundation. + * Copyright (c) 2018-2024, Laurence Lundblade. + * Copyright (c) 2021, Arm Limited. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * See BSD-3-Clause license in README.md + * + * Created on 11/28/24 from qcbor_decode.c + * ========================================================================== */ + + +#include "qcbor/qcbor_decode.h" +#include "qcbor/qcbor_spiffy_decode.h" +// TODO: see about removing these two includes +#include "qcbor/qcbor_tag_decode.h" +#include "ieee754.h" /* Does not use math.h */ +#include "decode_private.h" +#include "decode_nesting.h" + + + +#if (defined(__GNUC__) && !defined(__clang__)) +/* + * This is how the -Wmaybe-uninitialized compiler warning is + * handled. It can’t be ignored because some version of gcc enable it + * with -Wall which is a common and useful gcc warning option. It also + * can’t be ignored because it is the goal of QCBOR to compile clean + * out of the box in all environments. + * + * The big problem with -Wmaybe-uninitialized is that it generates + * false positives. It complains things are uninitialized when they + * are not. This is because it is not a thorough static analyzer. This + * is why “maybe” is in its name. The problem is it is just not + * thorough enough to understand all the code (and someone saw fit to + * put it in gcc and worse to enable it with -Wall). + * + * One solution would be to change the code so -Wmaybe-uninitialized + * doesn’t get confused, for example adding an unnecessary extra + * initialization to zero. (If variables were truly uninitialized, the + * correct path is to understand the code thoroughly and set them to + * the correct value at the correct time; in essence this is already + * done; -Wmaybe-uninitialized just can’t tell). This path is not + * taken because it makes the code bigger and is kind of the tail + * wagging the dog. + * + * The solution here is to just use a pragma to disable it for the + * whole file. Disabling it for each line makes the code fairly ugly + * requiring #pragma to push, pop and ignore. Another reason is the + * warnings issues vary by version of gcc and which optimization + * optimizations are selected. Another reason is that compilers other + * than gcc don’t have -Wmaybe-uninitialized. + * + * One may ask how to be sure these warnings are false positives and + * not real issues. 1) The code has been read carefully to check. 2) + * Testing is pretty thorough. 3) This code has been run through + * thorough high-quality static analyzers. + * + * In particularly, most of the warnings are about + * Item.Item->uDataType being uninitialized. QCBORDecode_GetNext() + * *always* sets this value and test case confirm + * this. -Wmaybe-uninitialized just can't tell. + * + * https://stackoverflow.com/questions/5080848/disable-gcc-may-be-used-uninitialized-on-a-particular-variable + */ +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + + +/* 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 bool +QCBORItem_MatchLabel(const QCBORItem Item1, const QCBORItem Item2) +{ + if(Item1.uLabelType == QCBOR_TYPE_INT64) { + if(Item2.uLabelType == QCBOR_TYPE_INT64 && Item1.label.int64 == Item2.label.int64) { + return true; + } +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + } 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; + } + } else if(Item1.uLabelType == QCBOR_TYPE_BYTE_STRING) { + if(Item2.uLabelType == QCBOR_TYPE_BYTE_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) { + return true; + } + } else if(Item1.uLabelType == QCBOR_TYPE_UINT64) { + if(Item2.uLabelType == QCBOR_TYPE_UINT64 && Item1.label.uint64 == Item2.label.uint64) { + return true; + } +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + } + + /* Other label types are never matched */ + return false; +} + + + +/* + Returns true if Item1 and Item2 are the same type + or if either are of QCBOR_TYPE_ANY. + */ +static bool +QCBORItem_MatchType(const QCBORItem Item1, const QCBORItem Item2) +{ + if(Item1.uDataType == Item2.uDataType) { + return true; + } else if(Item1.uDataType == QCBOR_TYPE_ANY) { + return true; + } else if(Item2.uDataType == QCBOR_TYPE_ANY) { + return true; + } + return false; +} + +/** + * @brief Rewind cursor to start as if map or array were just entered. + * + * @param[in] pMe The decoding context + * + * This affects the nesting tracking and the UsefulInputBuf. + */ +static void +QCBORDecode_Private_RewindMapOrArray(QCBORDecodeContext *pMe) +{ + /* Reset nesting tracking to the deepest bounded level */ + DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting)); + + DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting)); + + /* Reposition traversal cursor to the start of the map/array */ + UsefulInputBuf_Seek(&(pMe->InBuf), + DecodeNesting_GetMapOrArrayStart(&(pMe->nesting))); +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_Rewind(QCBORDecodeContext *pMe) +{ + if(pMe->nesting.pCurrentBounded != NULL) { + /* In a bounded map, array or bstr-wrapped CBOR */ + + if(DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_BYTE_STRING)) { + /* In bstr-wrapped CBOR. */ + + /* Reposition traversal cursor to start of wrapping byte string */ + UsefulInputBuf_Seek(&(pMe->InBuf), + pMe->nesting.pCurrentBounded->u.bs.uBstrStartOffset); + DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting)); + + } else { + /* In a map or array */ + QCBORDecode_Private_RewindMapOrArray(pMe); + } + + } else { + /* Not in anything bounded */ + + /* Reposition traversal cursor to the start of input CBOR */ + UsefulInputBuf_Seek(&(pMe->InBuf), 0ULL); + + /* Reset nesting tracking to beginning of input. */ + DecodeNesting_Init(&(pMe->nesting)); + } + + pMe->uLastError = QCBOR_SUCCESS; +} + + + + +/** + * @brief Search a map for a set of items. + * + * @param[in] pMe The decode context to search. + * @param[in,out] pItemArray The items to search for and the items found. + * @param[out] pInfo Several bits of meta-info returned by search. + * @param[in] pCallBack Callback object or @c NULL. + * + * @retval QCBOR_ERR_NOT_ENTERED Trying to search without entering a map. + * + * @retval QCBOR_ERR_DUPLICATE_LABEL Duplicate items (items with the same label) + * were found for one of the labels being + * search for. This duplicate detection is + * only performed for items in pItemArray, + * not every item in the map. + * + * @retval QCBOR_ERR_UNEXPECTED_TYPE A label was matched, but the type was + * wrong for the matchd label. + * + * @retval Also errors returned by QCBORDecode_GetNext(). + * + * On input, @c pItemArray contains a list of labels and data types of + * items to be found. + * + * On output, the fully retrieved items are filled in with values and + * such. The label was matched, so it never changes. + * + * If an item was not found, its data type is set to @ref QCBOR_TYPE_NONE. + * + * This also finds the ends of maps and arrays when they are exited. + */ +QCBORError +QCBORDecode_Private_MapSearch(QCBORDecodeContext *pMe, + QCBORItem *pItemArray, + MapSearchInfo *pInfo, + MapSearchCallBack *pCallBack) +{ + QCBORError uReturn; + uint64_t uFoundItemBitMap = 0; + + if(pMe->uLastError != QCBOR_SUCCESS) { + uReturn = pMe->uLastError; + goto Done2; + } + + if(!DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_MAP) && + pItemArray->uLabelType != QCBOR_TYPE_NONE) { + /* QCBOR_TYPE_NONE as first item indicates just looking + for the end of an array, so don't give error. */ + uReturn = QCBOR_ERR_MAP_NOT_ENTERED; + goto Done2; + } + + if(DecodeNesting_IsBoundedEmpty(&(pMe->nesting))) { + // It is an empty bounded array or map + if(pItemArray->uLabelType == QCBOR_TYPE_NONE) { + // Just trying to find the end of the map or array + pMe->uMapEndOffsetCache = DecodeNesting_GetMapOrArrayStart(&(pMe->nesting)); + uReturn = QCBOR_SUCCESS; + } else { + // Nothing is ever found in an empty array or map. All items + // are marked as not found below. + uReturn = QCBOR_SUCCESS; + } + goto Done2; + } + + QCBORDecodeNesting SaveNesting; + size_t uSavePos = UsefulInputBuf_Tell(&(pMe->InBuf)); + DecodeNesting_PrepareForMapSearch(&(pMe->nesting), &SaveNesting); + + /* Reposition to search from the start of the map / array */ + QCBORDecode_Private_RewindMapOrArray(pMe); + + /* + Loop over all the items in the map or array. Each item + could be a map or array, but label matching is only at + the main level. This handles definite- and indefinite- + length maps and arrays. The only reason this is ever + called on arrays is to find their end position. + + This will always run over all items in order to do + duplicate detection. + + This will exit with failure if it encounters an + unrecoverable error, but continue on for recoverable + errors. + + If a recoverable error occurs on a matched item, then + that error code is returned. + */ + const uint8_t uMapNestLevel = DecodeNesting_GetBoundedModeLevel(&(pMe->nesting)); + if(pInfo) { + pInfo->uItemCount = 0; + } + uint8_t uNextNestLevel; + do { + /* Remember offset of the item because sometimes it has to be returned */ + const size_t uOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); + + /* Get the item */ + QCBORItem Item; + /* QCBORDecode_Private_GetNextTagContent() rather than GetNext() + * because a label match is performed on recoverable errors to + * be able to return the the error code for the found item. */ + QCBORError uResult = QCBORDecode_Private_GetNextTagContent(pMe, &Item); + if(QCBORDecode_IsUnrecoverableError(uResult)) { + /* The map/array can't be decoded when unrecoverable errors occur */ + uReturn = uResult; + goto Done; + } + if(uResult == QCBOR_ERR_NO_MORE_ITEMS) { + /* Unexpected end of map or array. */ + uReturn = uResult; + goto Done; + } + + /* See if item has one of the labels that are of interest */ + bool bMatched = false; + for(int nIndex = 0; pItemArray[nIndex].uLabelType != QCBOR_TYPE_NONE; nIndex++) { + if(QCBORItem_MatchLabel(Item, pItemArray[nIndex])) { + /* A label match has been found */ + if(uFoundItemBitMap & (0x01ULL << nIndex)) { + uReturn = QCBOR_ERR_DUPLICATE_LABEL; + goto Done; + } + if(uResult != QCBOR_SUCCESS) { + /* The label matches, but the data item is in error. + * It is OK to have recoverable errors on items that + * are not matched. */ + uReturn = uResult; + goto Done; + } + if(!QCBORItem_MatchType(Item, pItemArray[nIndex])) { + /* The data item is not of the type(s) requested */ + uReturn = QCBOR_ERR_UNEXPECTED_TYPE; + goto Done; + } + + /* Successful match. Return the item. */ + pItemArray[nIndex] = Item; + uFoundItemBitMap |= 0x01ULL << nIndex; + if(pInfo) { + pInfo->uStartOffset = uOffset; + } + bMatched = true; + } + } + + + if(!bMatched && pCallBack != NULL) { + /* + Call the callback on unmatched labels. + (It is tempting to do duplicate detection here, but that would + require dynamic memory allocation because the number of labels + that might be encountered is unbounded.) + */ + uReturn = (*(pCallBack->pfCallback))(pCallBack->pCBContext, &Item); + if(uReturn != QCBOR_SUCCESS) { + goto Done; + } + } + + /* + Consume the item whether matched or not. This + does the work of traversing maps and array and + everything in them. In this loop only the + items at the current nesting level are examined + to match the labels. + */ + uReturn = QCBORDecode_Private_ConsumeItem(pMe, &Item, NULL, &uNextNestLevel); + if(uReturn != QCBOR_SUCCESS) { + goto Done; + } + + if(pInfo) { + pInfo->uItemCount++; + } + + } while (uNextNestLevel >= uMapNestLevel); + + uReturn = QCBOR_SUCCESS; + + const size_t uEndOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); + + // Check here makes sure that this won't accidentally be + // QCBOR_MAP_OFFSET_CACHE_INVALID which is larger than + // QCBOR_MAX_DECODE_INPUT_SIZE. + // Cast to uint32_t to possibly address cases where SIZE_MAX < UINT32_MAX + if((uint32_t)uEndOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) { + uReturn = QCBOR_ERR_INPUT_TOO_LARGE; + goto Done; + } + /* Cast OK because encoded CBOR is limited to UINT32_MAX */ + pMe->uMapEndOffsetCache = (uint32_t)uEndOffset; + + 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 */ + for(int i = 0; pItemArray[i].uLabelType != 0; i++) { + if(!(uFoundItemBitMap & (0x01ULL << i))) { + pItemArray[i].uDataType = QCBOR_TYPE_NONE; + pItemArray[i].uLabelType = QCBOR_TYPE_NONE; + } + } + + return uReturn; +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_SeekToLabelN(QCBORDecodeContext *pMe, int64_t nLabel) +{ + MapSearchInfo Info; + QCBORItem OneItemSeach[2]; + + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; + OneItemSeach[0].label.int64 = nLabel; + OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); + if(pMe->uLastError == QCBOR_SUCCESS) { + UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset); + } +} + + +void +QCBORDecode_SeekToLabelSZ(QCBORDecodeContext *pMe, const char *szLabel) +{ +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + MapSearchInfo Info; + QCBORItem OneItemSeach[2]; + + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; + OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); + OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); + if(pMe->uLastError == QCBOR_SUCCESS) { + UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset); + } +#else + (void)pMe; + (void)szLabel; + pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +} + + +void +QCBORDecode_Private_GetItemInMapNoCheck(QCBORDecodeContext *pMe, + QCBORItem *OneItemSeach, + QCBORItem *pItem, + size_t *puOffset) +{ + QCBORError uErr; + MapSearchInfo SearchInfo; + + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + uErr = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &SearchInfo, NULL); + + if(uErr == QCBOR_SUCCESS && OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) { + uErr = QCBOR_ERR_LABEL_NOT_FOUND; + } + *pItem = OneItemSeach[0]; + *puOffset = SearchInfo.uStartOffset; + + if(uErr == QCBOR_SUCCESS) { + QCBORDecode_Private_SaveTagNumbers(pMe, pItem); + } + + pMe->uLastError = (uint8_t)uErr; +} + + +static void +QCBORDecode_Private_GetItemInMap(QCBORDecodeContext *pMe, QCBORItem *OneItemSeach, QCBORItem *pItem) +{ + QCBORError uErr; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, &uOffset); + + uErr = QCBORDecode_Private_GetItemChecks(pMe, pMe->uLastError, uOffset, pItem); + if(uErr != QCBOR_SUCCESS) { + goto Done; + } + + QCBORDecode_Private_SaveTagNumbers(pMe, pItem); + +Done: + pMe->uLastError = (uint8_t)uErr; +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetItemInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uQcborType, + QCBORItem *pItem) +{ + QCBORItem OneItemSeach[2]; + + OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; + OneItemSeach[0].label.int64 = nLabel; + OneItemSeach[0].uDataType = uQcborType; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + QCBORDecode_Private_GetItemInMap(pMe, OneItemSeach, pItem); +} + + +/** + * @brief Get an item by label by type. + * + * @param[in] pMe The decode context. + * @param[in] nLabel The label to search map for. + * @param[in] uQcborType The QCBOR type to look for. + * @param[out] pItem The item found. + * @param[out] puOffset The offset of item for tag consumption check. + * + * This finds the item with the given label in currently open + * map. This does not call QCBORDecode_Private_GetItemChecks() + * to check tag number consumption or decode conformance. + */ +void +QCBORDecode_Private_GetItemInMapNoCheckN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uQcborType, + QCBORItem *pItem, + size_t *puOffset) +{ + QCBORItem OneItemSeach[2]; + + OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; + OneItemSeach[0].label.int64 = nLabel; + OneItemSeach[0].uDataType = uQcborType; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, puOffset); +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uQcborType, + QCBORItem *pItem) +{ +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + QCBORItem OneItemSeach[2]; + + OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; + OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); + OneItemSeach[0].uDataType = uQcborType; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + QCBORDecode_Private_GetItemInMap(pMe, OneItemSeach, pItem); + +#else + (void)pMe; + (void)szLabel; + (void)uQcborType; + (void)pItem; + pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +} + +/** + * @brief Get an item by string label of a particular type + * + * @param[in] pMe The decode context. + * @param[in] szLabel The label to search map for. + * @param[in] uQcborType The QCBOR type to look for. + * @param[out] pItem The item found. + * @param[out] puOffset The offset of item for tag consumption check. + * + * This finds the item with the given label in currently open + * map. This does not call QCBORDecode_Private_GetItemChecks() + * to check tag number consumption or decode conformance. + */ +void +QCBORDecode_Private_GetItemInMapNoCheckSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uQcborType, + QCBORItem *pItem, + size_t *puOffset) +{ +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + QCBORItem OneItemSeach[2]; + + OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; + OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); + OneItemSeach[0].uDataType = uQcborType; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + QCBORDecode_Private_GetItemInMapNoCheck(pMe, OneItemSeach, pItem, puOffset); + +#else + (void)pMe; + (void)szLabel; + (void)uQcborType; + (void)pItem; + (void)puOffset; + pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +} + + + + + + +/** + * @brief Semi-private. Get pointer, length and item count of an array or map. + * + * @param[in] pMe The decode context. + * @param[in] pTarget The label and type of the array or map to retrieve. + * @param[out] pItem The item for the array/map. + * @param[out] pEncodedCBOR Pointer and length of the encoded map or array. + * + * The next item to be decoded must be a map or array as specified by @c uType. + * + * When this is complete, the traversal cursor is unchanged. + */void +QCBORDecode_Private_SearchAndGetArrayOrMap(QCBORDecodeContext *pMe, + QCBORItem *pTarget, + QCBORItem *pItem, + UsefulBufC *pEncodedCBOR) +{ + MapSearchInfo Info; + QCBORDecodeNesting SaveNesting; + size_t uSaveCursor; + + pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pTarget, &Info, NULL); + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + pMe->uLastError = (uint8_t)QCBORDecode_Private_GetItemChecks(pMe, pMe->uLastError, Info.uStartOffset, pItem); + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + /* Save the whole position of things so they can be restored. + * so the cursor position is unchanged by this operation, like + * all the other GetXxxxInMap() operations. */ + DecodeNesting_PrepareForMapSearch(&(pMe->nesting), &SaveNesting); + uSaveCursor = UsefulInputBuf_Tell(&(pMe->InBuf)); + + DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting)); + UsefulInputBuf_Seek(&(pMe->InBuf), Info.uStartOffset); + QCBORDecode_Private_GetArrayOrMap(pMe, pTarget[0].uDataType, pItem, pEncodedCBOR); + + UsefulInputBuf_Seek(&(pMe->InBuf), uSaveCursor); + DecodeNesting_RestoreFromMapSearch(&(pMe->nesting), &SaveNesting); +} + + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemList) +{ + pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, NULL); +} + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetItemsInMapWithCallback(QCBORDecodeContext *pMe, + QCBORItem *pItemList, + void *pCallbackCtx, + QCBORItemCallback pfCB) +{ + MapSearchCallBack CallBack; + + CallBack.pCBContext = pCallbackCtx; + CallBack.pfCallback = pfCB; + + pMe->uLastError = (uint8_t)QCBORDecode_Private_MapSearch(pMe, pItemList, NULL, &CallBack); +} + + + + +/** + * @brief Search for a map/array by label and enter it + * + * @param[in] pMe The decode context. + * @param[in] pSearch The map/array to search for. + * + * @c pSearch is expected to contain one item of type map or array + * with the label specified. The current bounded map will be searched for + * this and if found will be entered. + * + * If the label is not found, or the item found is not a map or array, + * the error state is set. + */ +static void +QCBORDecode_Private_SearchAndEnter(QCBORDecodeContext *pMe, QCBORItem pSearch[]) +{ + QCBORError uErr; + MapSearchInfo SearchInfo; + + // The first item in pSearch is the one that is to be + // entered. It should be the only one filled in. Any other + // will be ignored unless it causes an error. + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + uErr = QCBORDecode_Private_MapSearch(pMe, pSearch, &SearchInfo, NULL); + + pMe->uLastError = (uint8_t)QCBORDecode_Private_GetItemChecks(pMe, uErr, SearchInfo.uStartOffset, pSearch); + + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + if(pSearch->uDataType == QCBOR_TYPE_NONE) { + pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; + return; + } + + + /* The map or array was found. Now enter it. + * + * QCBORDecode_EnterBoundedMapOrArray() used here, requires the + * next item for the pre-order traversal cursor to be the map/array + * found by MapSearch(). The next few lines of code force the + * cursor to that. + * + * There is no need to retain the old cursor because + * QCBORDecode_EnterBoundedMapOrArray() will set it to the + * beginning of the map/array being entered. + * + * The cursor is forced by: 1) setting the input buffer position to + * the item offset found by MapSearch(), 2) setting the map/array + * counter to the total in the map/array, 3) setting the nesting + * level. Setting the map/array counter to the total is not + * strictly correct, but this is OK because this cursor only needs + * to be used to get one item and MapSearch() has already found it + * confirming it exists. + */ + UsefulInputBuf_Seek(&(pMe->InBuf), SearchInfo.uStartOffset); + + DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting)); + + DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting)); + + QCBORDecode_Private_EnterBoundedMapOrArray(pMe, pSearch->uDataType, NULL); +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_EnterMapFromMapN(QCBORDecodeContext *pMe, int64_t nLabel) +{ + 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; + + /* The map to enter was found, now finish off entering it. */ + QCBORDecode_Private_SearchAndEnter(pMe, OneItemSeach); +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel) +{ +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + 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_SearchAndEnter(pMe, OneItemSeach); +#else + (void)szLabel; + pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +} + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_EnterArrayFromMapN(QCBORDecodeContext *pMe, int64_t nLabel) +{ + 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_SearchAndEnter(pMe, OneItemSeach); +} + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel) +{ +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + 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_SearchAndEnter(pMe, OneItemSeach); +#else + (void)szLabel; + pMe->uLastError = QCBOR_ERR_LABEL_NOT_FOUND; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +} + + +/** + * @brief Semi-private to do the the work for EnterMap() and EnterArray(). + * + * @param[in] pMe The decode context + * @param[in] uType QCBOR_TYPE_MAP or QCBOR_TYPE_ARRAY. + * @param[out] pItem The data item for the map or array entered. + * + * The next item in the traversal must be a map or array. This + * consumes that item and does the book keeping to enter the map or + * array. + */ +void +QCBORDecode_Private_EnterBoundedMapOrArray(QCBORDecodeContext *pMe, + const uint8_t uType, + QCBORItem *pItem) +{ + QCBORError uErr; + + /* Must only be called on maps and arrays. */ + if(pMe->uLastError != QCBOR_SUCCESS) { + // Already in error state; do nothing. + return; + } + + /* Get the data item that is the map or array being entered. */ + QCBORItem Item; + uErr = QCBORDecode_GetNext(pMe, &Item); + if(uErr != QCBOR_SUCCESS) { + goto Done; + } + + uint8_t uItemDataType = Item.uDataType; + +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY ) { + uItemDataType = QCBOR_TYPE_ARRAY; + } +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + + if(uItemDataType != uType) { + uErr = QCBOR_ERR_UNEXPECTED_TYPE; + goto Done; + } + + QCBORDecode_Private_SaveTagNumbers(pMe, &Item); + + + const bool bIsEmpty = (Item.uNextNestLevel <= Item.uNestingLevel); + if(bIsEmpty) { + if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) { + // Undo decrement done by QCBORDecode_GetNext() so the the + // the decrement when exiting the map/array works correctly + pMe->nesting.pCurrent->u.ma.uCountCursor++; + } + // Special case to increment nesting level for zero-length maps + // and arrays entered in bounded mode. + DecodeNesting_Descend(&(pMe->nesting), uType); + } + + pMe->uMapEndOffsetCache = QCBOR_MAP_OFFSET_CACHE_INVALID; + + uErr = DecodeNesting_EnterBoundedMapOrArray(&(pMe->nesting), bIsEmpty, + UsefulInputBuf_Tell(&(pMe->InBuf))); + + if(pItem != NULL) { + *pItem = Item; + } + +Done: + pMe->uLastError = (uint8_t)uErr; +} + + +/** + * @brief Exit a bounded map, array or bstr (semi-private). + * + * @param[in] pMe Decode context. + * @param[in] uEndOffset The input buffer offset of the end of item exited. + * + * @returns QCBOR_SUCCESS or an error code. + * + * This is the common work for exiting a level that is a bounded map, + * array or bstr wrapped CBOR. + * + * One chunk of work is to set up the pre-order traversal so it is at + * the item just after the bounded map, array or bstr that is being + * exited. This is somewhat complex. + * + * The other work is to level-up the bounded mode to next higest + * bounded mode or the top level if there isn't one. + */ +QCBORError +QCBORDecode_Private_ExitBoundedLevel(QCBORDecodeContext *pMe, + const uint32_t uEndOffset) +{ + QCBORError uErr; + + /* + * First the pre-order-traversal byte offset is positioned to the + * item just after the bounded mode item that was just consumed. + */ + UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset); + + /* + * Next, set the current nesting level to one above the bounded + * level that was just exited. + * + * DecodeNesting_CheckBoundedType() is always called before this + * and makes sure pCurrentBounded is valid. + */ + DecodeNesting_LevelUpCurrent(&(pMe->nesting)); + + /* + * This does the complex work of leveling up the pre-order + * traversal when the end of a map or array or another bounded + * level is reached. It may do nothing, or ascend all the way to + * the top level. + */ + uErr = QCBORDecode_Private_NestLevelAscender(pMe, NULL, false); + if(uErr != QCBOR_SUCCESS) { + goto Done; + } + + /* + * This makes the next highest bounded level the current bounded + * level. If there is no next highest level, then no bounded mode + * is in effect. + */ + DecodeNesting_LevelUpBounded(&(pMe->nesting)); + + pMe->uMapEndOffsetCache = QCBOR_MAP_OFFSET_CACHE_INVALID; + +Done: + return uErr; +} + + +/** + * @brief Get started exiting a map or array (semi-private) + * + * @param[in] pMe The decode context + * @param[in] uType QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP + * + * This does some work for map and array exiting (but not + * bstr exiting). Then QCBORDecode_Private_ExitBoundedLevel() + * is called to do the rest. + */ +void +QCBORDecode_Private_ExitBoundedMapOrArray(QCBORDecodeContext *pMe, + const uint8_t uType) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + /* Already in error state; do nothing. */ + return; + } + + QCBORError uErr; + + if(!DecodeNesting_IsBoundedType(&(pMe->nesting), uType)) { + uErr = QCBOR_ERR_EXIT_MISMATCH; + goto Done; + } + + /* + Have to set the offset to the end of the map/array + that is being exited. If there is no cached value, + from previous map search, then do a dummy search. + */ + if(pMe->uMapEndOffsetCache == QCBOR_MAP_OFFSET_CACHE_INVALID) { + QCBORItem Dummy; + Dummy.uLabelType = QCBOR_TYPE_NONE; + uErr = QCBORDecode_Private_MapSearch(pMe, &Dummy, NULL, NULL); + if(uErr != QCBOR_SUCCESS) { + goto Done; + } + } + + uErr = QCBORDecode_Private_ExitBoundedLevel(pMe, pMe->uMapEndOffsetCache); + +Done: + pMe->uLastError = (uint8_t)uErr; +} + + + + + +/** + * @brief Process simple type true and false, a boolean + * + * @param[in] pMe The decode context. + * @param[in] pItem The item with either true or false. + * @param[out] pBool The boolean value output. + * + * Sets the internal error if the item isn't a true or a false. Also + * records any tag numbers as the tag numbers of the last item. + */ +static void +QCBORDecode_Private_ProcessBool(QCBORDecodeContext *pMe, + const QCBORItem *pItem, + bool *pBool) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + /* Already in error state, do nothing */ + return; + } + + switch(pItem->uDataType) { + case QCBOR_TYPE_TRUE: + *pBool = true; + break; + + case QCBOR_TYPE_FALSE: + *pBool = false; + break; + + default: + pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; + break; + } +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetBool(QCBORDecodeContext *pMe, bool *pValue) +{ + QCBORItem Item; + QCBORDecode_VGetNext(pMe, &Item); + QCBORDecode_Private_ProcessBool(pMe, &Item, pValue); +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetBoolInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + bool *pValue) +{ + QCBORItem Item; + QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item); + QCBORDecode_Private_ProcessBool(pMe, &Item, pValue); +} + + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetBoolInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + bool *pValue) +{ + QCBORItem Item; + QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item); + QCBORDecode_Private_ProcessBool(pMe, &Item, pValue); +} + + +/** + * @brief Process simple values. + * + * @param[in] pMe The decode context. + * @param[in] pItem The item with the simple value. + * @param[out] puSimple The simple value output. + * + * Sets the internal error if the item isn't a true or a false. Also + * records any tag numbers as the tag numbers of the last item. + */ +static void +QCBORDecode_Private_ProcessSimple(QCBORDecodeContext *pMe, + const QCBORItem *pItem, + uint8_t *puSimple) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + /* It's kind of lame to remap true...undef back to simple values, but + * this function isn't used much and to not do it would require + * changing GetNext() behavior in an incompatible way. + */ + switch(pItem->uDataType) { + case QCBOR_TYPE_UKNOWN_SIMPLE: + *puSimple = pItem->val.uSimple; + break; + + case QCBOR_TYPE_TRUE: + *puSimple = CBOR_SIMPLEV_TRUE; + break; + + case QCBOR_TYPE_FALSE: + *puSimple = CBOR_SIMPLEV_FALSE; + break; + + case QCBOR_TYPE_NULL: + *puSimple = CBOR_SIMPLEV_NULL; + break; + + case QCBOR_TYPE_UNDEF: + *puSimple = CBOR_SIMPLEV_UNDEF; + break; + + default: + pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; + return; + } +} + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetSimple(QCBORDecodeContext *pMe, uint8_t *puSimple) +{ + QCBORItem Item; + QCBORDecode_VGetNext(pMe, &Item); + QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimple); +} + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetSimpleInMapN(QCBORDecodeContext *pMe, + int64_t nLabel, + uint8_t *puSimpleValue) +{ + QCBORItem Item; + QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item); + QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimpleValue); +} + +/* + * Public function, see header qcbor/qcbor_decode.h file + */ +void +QCBORDecode_GetSimpleInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t *puSimpleValue) +{ + QCBORItem Item; + QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item); + QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimpleValue); +} + + + + + + +// Improvement: add methods for wrapped CBOR, a simple alternate +// to EnterBstrWrapped + diff --git a/src/qcbor_tag_decode.c b/src/qcbor_tag_decode.c index 31f9c03..2627962 100644 --- a/src/qcbor_tag_decode.c +++ b/src/qcbor_tag_decode.c @@ -10,13 +10,1029 @@ * Created on 9/5/24 from qcbode_decode.c * ========================================================================== */ -// TODO: qcbor_tag_decode.c or tag_decode.c - #include "qcbor/qcbor_tag_decode.h" +#include "decode_private.h" +#include "decode_nesting.h" #include /* For isnan() */ + +#ifndef QCBOR_DISABLE_TAGS + +/* Public function; see qcbor_tag_decode.h */ +QCBORError +QCBORDecode_GetNextTagNumber(QCBORDecodeContext *pMe, uint64_t *puTagNumber) +{ + QCBORItem Item; + size_t uOffset; + QCBORError uErr; + + const QCBORDecodeNesting SaveNesting = pMe->nesting; + const UsefulInputBuf Save = pMe->InBuf; + + uOffset = UsefulInputBuf_Tell(&(pMe->InBuf)); + if(uOffset == pMe->uTagNumberCheckOffset) { + pMe->uTagNumberIndex++; + } else { + pMe->uTagNumberIndex = 0; + } + + *puTagNumber = CBOR_TAG_INVALID64; + uErr = QCBORDecode_Private_GetNextTagContent(pMe, &Item); + if(uErr) { + return uErr; + } + + *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, &Item, pMe->uTagNumberIndex); + if(*puTagNumber == CBOR_TAG_INVALID64 || + QCBORDecode_GetNthTagNumber(pMe, &Item, pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) { + pMe->uTagNumberIndex = QCBOR_ALL_TAGS_PROCESSED; + } + pMe->uTagNumberCheckOffset = uOffset; + + pMe->nesting = SaveNesting; + pMe->InBuf = Save; + + return QCBOR_SUCCESS; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_VGetNextTagNumber(QCBORDecodeContext *pMe, uint64_t *puTagNumber) +{ + pMe->uLastError = (uint8_t)QCBORDecode_GetNextTagNumber(pMe, puTagNumber); +} + +/* + * Public function, see header qcbor/qcbor_tag_decode.h file + */ +QCBORError +QCBORDecode_GetNextTagNumberInMapN(QCBORDecodeContext *pMe, const int64_t nLabel, uint64_t *puTagNumber) +{ + size_t uOffset; + MapSearchInfo Info; + QCBORItem OneItemSeach[2]; + QCBORError uReturn; + + if(pMe->uLastError != QCBOR_SUCCESS) { + return pMe->uLastError; + } + + OneItemSeach[0].uLabelType = QCBOR_TYPE_INT64; + OneItemSeach[0].label.int64 = nLabel; + OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); + + uOffset = Info.uStartOffset; + if(uOffset == pMe->uTagNumberCheckOffset) { + pMe->uTagNumberIndex++; + } else { + pMe->uTagNumberIndex = 0; + } + + *puTagNumber = CBOR_TAG_INVALID64; + + *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, + &OneItemSeach[0], + pMe->uTagNumberIndex); + if(*puTagNumber == CBOR_TAG_INVALID64 || + QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) { + pMe->uTagNumberIndex = QCBOR_ALL_TAGS_PROCESSED; + } + pMe->uTagNumberCheckOffset = uOffset; + + return uReturn; +} + + +/* Public function; see qcbor_tag_decode.h */ +QCBORError +QCBORDecode_GetNextTagNumberInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, uint64_t *puTagNumber) +{ +#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS + size_t uOffset; + MapSearchInfo Info; + QCBORItem OneItemSeach[2]; + + if(pMe->uLastError != QCBOR_SUCCESS) { + return pMe->uLastError; + } + + OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING; + OneItemSeach[0].label.string = UsefulBuf_FromSZ(szLabel); + OneItemSeach[0].uDataType = QCBOR_TYPE_ANY; + OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array + + QCBORError uReturn = QCBORDecode_Private_MapSearch(pMe, OneItemSeach, &Info, NULL); + + + uOffset = Info.uStartOffset; + if(uOffset == pMe->uTagNumberCheckOffset) { + pMe->uTagNumberIndex++; + } else { + pMe->uTagNumberIndex = 0; + } + + *puTagNumber = CBOR_TAG_INVALID64; + + *puTagNumber = QCBORDecode_GetNthTagNumber(pMe, + &OneItemSeach[0], + pMe->uTagNumberIndex); + if(*puTagNumber == CBOR_TAG_INVALID64 || + QCBORDecode_GetNthTagNumber(pMe, &OneItemSeach[0], pMe->uTagNumberIndex+1) == CBOR_TAG_INVALID64 ) { + pMe->uTagNumberIndex = 255; /* All tags clear for this item */ + } + pMe->uTagNumberCheckOffset = uOffset; + + return uReturn; +#else /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ + (void)pMe; + (void)szLabel; + (void)puTagNumber; + return QCBOR_ERR_LABEL_NOT_FOUND; +#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */ +} + + +/* Public function; see qcbor_tag_decode.h */ +uint64_t +QCBORDecode_GetNthTagNumber(const QCBORDecodeContext *pMe, + const QCBORItem *pItem, + uint8_t uIndex) +{ + if(pItem->uDataType == QCBOR_TYPE_NONE) { + return CBOR_TAG_INVALID64; + } + if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) { + return CBOR_TAG_INVALID64; + } + + return QCBORDecode_Private_UnMapTagNumber(pMe, pItem->auTagNumbers[uIndex]); +} + + +/* Public function; see qcbor_tag_decode.h */ +uint64_t +QCBORDecode_GetNthTagNumberOfLast(QCBORDecodeContext *pMe, uint8_t uIndex) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + return CBOR_TAG_INVALID64; + } + if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) { + return CBOR_TAG_INVALID64; + } + + return QCBORDecode_Private_UnMapTagNumber(pMe, pMe->auLastTags[uIndex]); +} + + +static uint64_t +QCBORDecode_Private_GetNthTagNumberReverse(const QCBORDecodeContext *pMe, + const uint16_t puTagNumbers[], + const uint32_t uIndex) +{ + uint32_t uArrayIndex; + + /* Find number of tag numbers */ + for(uArrayIndex = QCBOR_MAX_TAGS_PER_ITEM-1; uArrayIndex > 0; uArrayIndex--) { + if(puTagNumbers[uArrayIndex] != CBOR_TAG_INVALID16) { + break; + } + } + if(uIndex > uArrayIndex) { + return CBOR_TAG_INVALID64; + } + + return QCBORDecode_Private_UnMapTagNumber(pMe, puTagNumbers[uArrayIndex - uIndex]); +} + + +/* Public function; see qcbor_tag_decode.h */ +uint64_t +QCBORDecode_GetNthTag(QCBORDecodeContext *pMe, + const QCBORItem *pItem, + const uint32_t uIndex) +{ + if(pItem->uDataType == QCBOR_TYPE_NONE) { + return CBOR_TAG_INVALID64; + } + + return QCBORDecode_Private_GetNthTagNumberReverse(pMe, + pItem->auTagNumbers, + uIndex); +} + + +/* Public function; see qcbor_tag_decode.h */ +uint64_t +QCBORDecode_GetNthTagOfLast(const QCBORDecodeContext *pMe, uint32_t uIndex) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + return CBOR_TAG_INVALID64; + } + if(uIndex >= QCBOR_MAX_TAGS_PER_ITEM) { + return CBOR_TAG_INVALID64; + } + + return QCBORDecode_Private_GetNthTagNumberReverse(pMe, + pMe->auLastTags, + uIndex); +} + + + +// TODO: uTagNumber might be better a list than calling this multiple times +static QCBORError +QCBORDecode_Private_Check1TagNumber(const QCBORDecodeContext *pMe, + const QCBORItem *pItem, + const uint64_t uTagNumber, + const size_t uOffset) +{ + if(pItem->auTagNumbers[0] == CBOR_TAG_INVALID16) { + /* There are no tag numbers at all, so no unprocessed */ + return QCBOR_SUCCESS; + } + + /* There are some tag numbers, so keep checking. This check passes + * if there is one and only one tag number that matches uTagNumber + */ + + // TODO: behave different in v1 and v2? + + const uint64_t uInnerTag = QCBORDecode_GetNthTagNumber(pMe, pItem, 0); + + if(uInnerTag == uTagNumber && pItem->auTagNumbers[1] == CBOR_TAG_INVALID16) { + /* The only tag number is the one we are processing so no unprocessed */ + return QCBOR_SUCCESS; + } + + if(uOffset != pMe->uTagNumberCheckOffset) { + /* processed tag numbers are for some other item, not us */ + return QCBOR_ERR_UNPROCESSED_TAG_NUMBER; + } + + if(pMe->uTagNumberIndex != 1) { + return QCBOR_ERR_UNPROCESSED_TAG_NUMBER; + } + + return QCBOR_SUCCESS; +} +#endif /* ! QCBOR_DISABLE_TAGS */ + + +static QCBORError +QCBORDecode_Private_CheckTagNType(QCBORDecodeContext *pMe, + const QCBORItem *pItem, + const size_t uOffset, + const uint8_t *uQCBORTypes, + const uint64_t *uTagNumbers, + const uint8_t uTagRequirement, + bool *bTypeMatched) +{ + const uint64_t *pQType; + const uint64_t *pTNum; + const uint8_t *pTypeNum; + + const int nTagReq = uTagRequirement & ~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS; + + *bTypeMatched = false; + for(pTypeNum = uQCBORTypes; *pTypeNum != QCBOR_TYPE_NONE; pTypeNum++) { + if(pItem->uDataType == *pTypeNum) { + *bTypeMatched = true; + break; + } + } + +#ifndef QCBOR_DISABLE_TAGS + bool bTagNumberMatched; + QCBORError uErr; + const uint64_t uInnerTag = QCBORDecode_GetNthTagNumber(pMe, pItem, 0); + + bTagNumberMatched = false; + for(pQType = uTagNumbers; *pQType != CBOR_TAG_INVALID64; pQType++) { + if(uInnerTag == *pQType) { + bTagNumberMatched = true; + break; + } + } + + + if(nTagReq == QCBOR_TAG_REQUIREMENT_TAG) { + /* There must be a tag number */ + if(!bTagNumberMatched && !*bTypeMatched) { + return QCBOR_ERR_UNEXPECTED_TYPE; // TODO: error code + } + + } else if(nTagReq == QCBOR_TAG_REQUIREMENT_NOT_A_TAG) { + if(bTagNumberMatched || *bTypeMatched) { + return QCBOR_ERR_UNEXPECTED_TYPE; // TODO: error code + } + + } else if(nTagReq == QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG) { + /* No check necessary */ + } + + /* Now check if there are extra tags and if there's an error in them */ + if(!(uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS)) { + /* The flag to ignore extra is not set, so keep checking */ + for(pTNum = uTagNumbers; *pTNum != CBOR_TAG_INVALID64; pTNum++) { + uErr = QCBORDecode_Private_Check1TagNumber(pMe, pItem, *pTNum, uOffset); + if(uErr != QCBOR_SUCCESS) { + return uErr; + } + } + } + + return QCBOR_SUCCESS; +#else /* ! QCBOR_DISABLE_TAGS */ + (void)pMe; + (void)uOffset; + (void)uTagNumbers; + + if(nTagReq != QCBOR_TAG_REQUIREMENT_TAG && bTypeMatched) { + return QCBOR_SUCCESS; + } else { + return QCBOR_ERR_UNEXPECTED_TYPE; + } + +#endif /* ! QCBOR_DISABLE_TAGS */ + +} + + +void +QCBORDecode_Private_ProcessTagItemMulti(QCBORDecodeContext *pMe, + QCBORItem *pItem, + const uint8_t uTagRequirement, + const uint8_t uQCBORTypes[], + const uint64_t uTagNumbers[], + QCBORTagContentCallBack *pfCB, + size_t uOffset) +{ + QCBORError uErr; + bool bTypeMatched; + + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + uErr = QCBORDecode_Private_CheckTagNType(pMe, + pItem, + uOffset, + uQCBORTypes, + uTagNumbers, + uTagRequirement, + &bTypeMatched); + if(uErr != QCBOR_SUCCESS) { + goto Done; + } + + if(!bTypeMatched) { + /* Tag content wasn't previously processed, do it now */ + uErr = (*pfCB)(pMe, NULL, uTagNumbers[0], pItem); + if(uErr != QCBOR_SUCCESS) { + goto Done; + } + } + +Done: + pMe->uLastError = (uint8_t)uErr; +} + + +/* + **/ +void +QCBORDecode_Private_ProcessTagItem(QCBORDecodeContext *pMe, + QCBORItem *pItem, + const uint8_t uTagRequirement, + const uint8_t uQCBORTypes[], + const uint64_t uTagNumber, + QCBORTagContentCallBack *pfCB, + size_t uOffset) +{ + uint64_t auTagNumbers[2]; + + auTagNumbers[0] = uTagNumber; + auTagNumbers[1] = CBOR_TAG_INVALID64; + + QCBORDecode_Private_ProcessTagItemMulti(pMe, + pItem, + uTagRequirement, + uQCBORTypes, + auTagNumbers, + pfCB, + uOffset); +} + + +static void +QCBORDecode_Private_ProcessTagOne(QCBORDecodeContext *pMe, + QCBORItem *pItem, + const uint8_t uTagRequirement, + const uint8_t uQCBORType, + const uint64_t uTagNumber, + QCBORTagContentCallBack *pfCB, + const size_t uOffset) +{ + uint8_t auQCBORType[2]; + + auQCBORType[0] = uQCBORType; + auQCBORType[1] = QCBOR_TYPE_NONE; + + QCBORDecode_Private_ProcessTagItem(pMe, + pItem, + uTagRequirement, + auQCBORType, + uTagNumber, + pfCB, + uOffset); +} + + +void +QCBORDecode_Private_GetTaggedString(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + const uint8_t uQCBOR_Type, + const uint64_t uTagNumber, + UsefulBufC *pStr) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + uQCBOR_Type, + uTagNumber, + QCBORDecode_StringsTagCB, + uOffset); + + if(pMe->uLastError == QCBOR_SUCCESS) { + *pStr = Item.val.string; + } else { + *pStr = NULLUsefulBufC; + } +} + + +/** + * @brief Semi-private to get an string by label to match a tag specification. + * + * @param[in] pMe The decode context. + * @param[in] nLabel Label to search map for. + * @param[in] uTagRequirement Whether or not tag number is required. + * See @ref QCBOR_TAG_REQUIREMENT_TAG. + * @param[in] uQCBOR_Type QCBOR type to search for. + * @param[in] uTagNumber Tag number to match. + * @param[out] pString The string found. + * + * This finds the string with the given label in currently open + * map. Then checks that its tag number and types matches the tag + * specification. If not, an error is set in the decode context. + */ +void +QCBORDecode_Private_GetTaggedStringInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + const uint8_t uQCBOR_Type, + const uint64_t uTagNumber, + UsefulBufC *pString) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckN(pMe, + nLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + uQCBOR_Type, + uTagNumber, + QCBORDecode_StringsTagCB, + uOffset); + + if(pMe->uLastError == QCBOR_SUCCESS) { + *pString = Item.val.string; + } +} + + +/** + * @brief Semi-private to get an string by label to match a tag specification. + * + * @param[in] pMe The decode context. + * @param[in] szLabel Label to search map for. + * @param[in] uTagRequirement Whether or not tag number is required. + * See @ref QCBOR_TAG_REQUIREMENT_TAG. + * @param[in] uQCBOR_Type QCBOR type to search for. + * @param[in] uTagNumber Tag number to match. + * @param[out] pString The string found. + * + * This finds the string with the given label in currently open + * map. Then checks that its tag number and types matches the tag + * specification. If not, an error is set in the decode context. + */ +void +QCBORDecode_Private_GetTaggedStringInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + uint8_t uQCBOR_Type, + uint64_t uTagNumber, + UsefulBufC *pString) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, + szLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + uQCBOR_Type, + uTagNumber, + QCBORDecode_StringsTagCB, + uOffset); + + + if(pMe->uLastError == QCBOR_SUCCESS) { + *pString = Item.val.string; + } +} + + + + +/** + * @brief The main work of entering some byte-string wrapped CBOR. + * + * @param[in] pMe The decode context. + * @param[in] pItem The byte string item. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX + * @param[out] pBstr Pointer and length of byte string entered. + * + * This is called once the byte string item has been decoded to do all + * the book keeping work for descending a nesting level into the + * nested CBOR. + * + * See QCBORDecode_EnterBstrWrapped() for details on uTagRequirement. + */ +static QCBORError +QCBORDecode_Private_EnterBstrWrapped(QCBORDecodeContext *pMe, + const QCBORItem *pItem, + const uint8_t uTagRequirement, + const size_t uOffset, + UsefulBufC *pBstr) +{ + bool bTypeMatched; + QCBORError uError; + + const uint8_t uTypes[] = {QBCOR_TYPE_WRAPPED_CBOR, + QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE, + QCBOR_TYPE_NONE}; + const uint64_t uTagNumbers[] = {CBOR_TAG_CBOR, + CBOR_TAG_CBOR_SEQUENCE, + CBOR_TAG_INVALID64}; + + + if(pBstr) { + *pBstr = NULLUsefulBufC; + } + + if(pMe->uLastError != QCBOR_SUCCESS) { + return pMe->uLastError; + } + + if(pItem->uDataAlloc) { + return QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING; + } + + uError = QCBORDecode_Private_CheckTagNType(pMe, + pItem, + uOffset, + uTypes,//TODO: maybe empty? + uTagNumbers, + uTagRequirement, + &bTypeMatched); + + if(pItem->uDataType != QCBOR_TYPE_BYTE_STRING) { + uError = QCBOR_ERR_BAD_TAG_CONTENT; // TODO: error + } + + + if(DecodeNesting_IsCurrentDefiniteLength(&(pMe->nesting))) { + /* Reverse the decrement done by GetNext() for the bstr so the + * increment in QCBORDecode_NestLevelAscender() called by + * ExitBoundedLevel() will work right. + */ + DecodeNesting_ReverseDecrement(&(pMe->nesting)); + } + + if(pBstr) { + *pBstr = pItem->val.string; + } + + /* This saves the current length of the UsefulInputBuf and then + * narrows the UsefulInputBuf to start and length of the wrapped + * CBOR that is being entered. + * + * Most of these calls are simple inline accessors so this doesn't + * amount to much code. + */ + + const size_t uPreviousLength = UsefulInputBuf_GetBufferLength(&(pMe->InBuf)); + /* This check makes the cast of uPreviousLength to uint32_t below safe. */ + if(uPreviousLength >= QCBOR_MAX_DECODE_INPUT_SIZE) { + uError = QCBOR_ERR_INPUT_TOO_LARGE; + goto Done; + } + + const size_t uStartOfBstr = UsefulInputBuf_PointerToOffset(&(pMe->InBuf), + pItem->val.string.ptr); + /* This check makes the cast of uStartOfBstr to uint32_t below safe. */ + if(uStartOfBstr == SIZE_MAX || uStartOfBstr > QCBOR_MAX_DECODE_INPUT_SIZE) { + /* This should never happen because pItem->val.string.ptr should + * always be valid since it was just returned. + */ + uError = QCBOR_ERR_INPUT_TOO_LARGE; + goto Done; + } + + const size_t uEndOfBstr = uStartOfBstr + pItem->val.string.len; + + UsefulInputBuf_Seek(&(pMe->InBuf), uStartOfBstr); + UsefulInputBuf_SetBufferLength(&(pMe->InBuf), uEndOfBstr); + + uError = DecodeNesting_DescendIntoBstrWrapped(&(pMe->nesting), + (uint32_t)uPreviousLength, + (uint32_t)uStartOfBstr); +Done: + return uError; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pBstr) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); + pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe, + &Item, + uTagRequirement, + uOffset, + pBstr); +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pBstr) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckN(pMe, + nLabel, + QCBOR_TYPE_BYTE_STRING, + &Item, + &uOffset); + pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe, + &Item, + uTagRequirement, + uOffset, + pBstr); +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pBstr) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, + szLabel, + QCBOR_TYPE_BYTE_STRING, + &Item, + &uOffset); + pMe->uLastError = (uint8_t)QCBORDecode_Private_EnterBstrWrapped(pMe, + &Item, + uTagRequirement, + uOffset, + pBstr); +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pMe) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + // Already in error state; do nothing. + return; + } + + if(!DecodeNesting_IsBoundedType(&(pMe->nesting), QCBOR_TYPE_BYTE_STRING)) { + pMe->uLastError = QCBOR_ERR_EXIT_MISMATCH; + return; + } + + const uint32_t uEndOfBstr = (uint32_t)UsefulInputBuf_GetBufferLength(&(pMe->InBuf)); + + /* + Reset the length of the UsefulInputBuf to what it was before + the bstr wrapped CBOR was entered. + */ + UsefulInputBuf_SetBufferLength(&(pMe->InBuf), + DecodeNesting_GetPreviousBoundedEnd(&(pMe->nesting))); + + + QCBORError uErr = QCBORDecode_Private_ExitBoundedLevel(pMe, uEndOfBstr); + pMe->uLastError = (uint8_t)uErr; +} + + + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetEpochDate(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + int64_t *pnTime) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + QCBOR_TYPE_DATE_EPOCH, + CBOR_TAG_DATE_EPOCH, + QCBORDecode_DateEpochTagCB, + uOffset); + *pnTime = Item.val.epochDate.nSeconds; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetEpochDateInMapN(QCBORDecodeContext *pMe, + int64_t nLabel, + uint8_t uTagRequirement, + int64_t *pnTime) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckN(pMe, + nLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + QCBOR_TYPE_DATE_EPOCH, + CBOR_TAG_DATE_EPOCH, + QCBORDecode_DateEpochTagCB, + uOffset); + *pnTime = Item.val.epochDate.nSeconds; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetEpochDateInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + int64_t *pnTime) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, + szLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + QCBOR_TYPE_DATE_EPOCH, + CBOR_TAG_DATE_EPOCH, + QCBORDecode_DateEpochTagCB, + uOffset); + *pnTime = Item.val.epochDate.nSeconds; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetEpochDays(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + int64_t *pnDays) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + QCBOR_TYPE_DAYS_EPOCH, + CBOR_TAG_DAYS_EPOCH, + QCBORDecode_DaysEpochTagCB, + uOffset); + *pnDays = Item.val.epochDays; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetEpochDaysInMapN(QCBORDecodeContext *pMe, + int64_t nLabel, + uint8_t uTagRequirement, + int64_t *pnDays) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckN(pMe, + nLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + QCBOR_TYPE_DAYS_EPOCH, + CBOR_TAG_DAYS_EPOCH, + QCBORDecode_DaysEpochTagCB, + uOffset); + *pnDays = Item.val.epochDays; +} + + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetEpochDaysInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + int64_t *pnDays) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, + szLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_ProcessTagOne(pMe, + &Item, + uTagRequirement, + QCBOR_TYPE_DAYS_EPOCH, + CBOR_TAG_DAYS_EPOCH, + QCBORDecode_DaysEpochTagCB, + uOffset); + *pnDays = Item.val.epochDays; +} + + + + +static void +QCBORDecode_Private_GetMIME(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + QCBORItem *pItem, + UsefulBufC *pValue, + bool *pbIsTag257, + size_t uOffset) +{ + QCBORError uErr; + + const uint8_t puTypes[] = {QCBOR_TYPE_MIME, QCBOR_TYPE_BINARY_MIME, QCBOR_TYPE_NONE}; + + const uint64_t puTNs[] = {CBOR_TAG_MIME, CBOR_TAG_BINARY_MIME, CBOR_TAG_INVALID64}; + + QCBORDecode_Private_ProcessTagItemMulti(pMe, + pItem, + uTagRequirement, + puTypes, + puTNs, + QCBORDecode_MIMETagCB, + uOffset); + if(pMe->uLastError) { + return; + } + + if(pItem->uDataType == QCBOR_TYPE_MIME) { + *pbIsTag257 = false; + } else if(pItem->uDataType == QCBOR_TYPE_BINARY_MIME) { + *pbIsTag257 = true; + } + *pValue = pItem->val.string; + + + uErr = QCBOR_SUCCESS; + + pMe->uLastError = (uint8_t)uErr; +} + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetMIMEMessage(QCBORDecodeContext *pMe, + const uint8_t uTagRequirement, + UsefulBufC *pMessage, + bool *pbIsTag257) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetAndTell(pMe, &Item, &uOffset); + QCBORDecode_Private_GetMIME(pMe, + uTagRequirement, + &Item, + pMessage, + pbIsTag257, + uOffset); +} + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetMIMEMessageInMapN(QCBORDecodeContext *pMe, + const int64_t nLabel, + const uint8_t uTagRequirement, + UsefulBufC *pMessage, + bool *pbIsTag257) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckN(pMe, + nLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_GetMIME(pMe, + uTagRequirement, + &Item, + pMessage, + pbIsTag257, + uOffset); +} + +/* Public function; see qcbor_tag_decode.h */ +void +QCBORDecode_GetMIMEMessageInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + const uint8_t uTagRequirement, + UsefulBufC *pMessage, + bool *pbIsTag257) +{ + QCBORItem Item; + size_t uOffset; + + QCBORDecode_Private_GetItemInMapNoCheckSZ(pMe, + szLabel, + QCBOR_TYPE_ANY, + &Item, + &uOffset); + QCBORDecode_Private_GetMIME(pMe, + uTagRequirement, + &Item, + pMessage, + pbIsTag257, + uOffset); +} + + + + /* Public function; see qcbor_tag_decode.h */ QCBORError QCBORDecode_DateEpochTagCB(QCBORDecodeContext *pDecodeCtx, @@ -166,7 +1182,8 @@ QCBORDecode_DaysEpochTagCB(QCBORDecodeContext *pDecodeCtx, * @ref CBOR_TAG_BIG_FLOAT. * @param[in] pDecodedItem Item being decoded. * - * @returns One of the ten values related to @ref QCBOR_TYPE_DECIMAL_FRACTION and @ref QCBOR_TYPE_BIGFLOAT + * @returns One of the ten values related to @ref QCBOR_TYPE_DECIMAL_FRACTION + * and @ref QCBOR_TYPE_BIGFLOAT * * Does mapping between a CBOR tag number and a QCBOR type with a * little logic and arithmetic.