blob: 68fa1b0896a3dd3e726249bea8c0c65ec9c656dd [file] [log] [blame]
/* ===========================================================================
* qcbor_main_decode.h -- The main CBOR decoder.
*
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2024, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited.
* All rights reserved.
*
* 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.
* ========================================================================= */
#include "qcbor/qcbor_main_decode.h"
#include "ieee754.h" /* Does not use math.h */
#include "decode_nesting.h"
#include "qcbor/qcbor_tag_decode.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
static bool
QCBORItem_IsMapOrArray(const QCBORItem Item)
{
const uint8_t uDataType = Item.uDataType;
return uDataType == QCBOR_TYPE_MAP ||
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
uDataType == QCBOR_TYPE_MAP_AS_ARRAY ||
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
uDataType == QCBOR_TYPE_ARRAY;
}
static bool
QCBORItem_IsEmptyDefiniteLengthMapOrArray(const QCBORItem Item)
{
if(!QCBORItem_IsMapOrArray(Item)){
return false;
}
if(Item.val.uCount != 0) {
return false;
}
return true;
}
static bool
QCBORItem_IsIndefiniteLengthMapOrArray(const QCBORItem Item)
{
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
if(!QCBORItem_IsMapOrArray(Item)){
return false;
}
if(Item.val.uCount != QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH) {
return false;
}
return true;
#else /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
(void)Item;
return false;
#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
}
#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_main_decode.h for definition of the object
used here: QCBORDecodeContext
===========================================================================*/
/* Public function; see qcbor_main_decode.h */
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->auMappedTagNumbers to CBOR_TAG_INVALID16. See
* QCBORDecode_Private_GetNextTagNumber() and
* QCBORDecode_Private_MapTagNumber(). */
memset(pMe->auMappedTagNumbers, 0xff, sizeof(pMe->auMappedTagNumbers));
pMe->uTagNumberCheckOffset = SIZE_MAX;
}
/*
* 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 16-bits or less 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,
QCBORDecodeMode uConfigFlags,
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;
}
}
}
}
#else
(void)uConfigFlags;
#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 /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
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 */
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
static QCBORError
QCBORDecode_Private_HalfConformance(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
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;
#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
const QCBORDecodeMode uDecodeMode = pMe->uDecodeMode;
#else /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
/* No decode conformance; this saves 100 bytes of object code */
const QCBORDecodeMode uDecodeMode = 0;
#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
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),
uDecodeMode,
&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(pMe->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->auMappedTagNumbers[uTagMapIndex] == CBOR_TAG_INVALID64) {
break;
}
if(pMe->auMappedTagNumbers[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->auMappedTagNumbers[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()
*/
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->auMappedTagNumbers[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;
#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
if(puLabelEndOffset != NULL) {
/* Cast is OK because lengths are all 32-bit in QCBOR */
*puLabelEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
}
#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
/* 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
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)
{
QCBORError uReturn;
QCBORItem Peek;
size_t uPeek;
*pbNextIsBreak = false;
if(UsefulInputBuf_BytesUnconsumed(&(pMe->InBuf)) != 0) {
uPeek = UsefulInputBuf_Tell(&(pMe->InBuf));
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
*/
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 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.
*/
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.
*/
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 */
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 qcbor_main_decode.h */
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 qcbor_main_decode.h */
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 qcbor_main_decode.h */
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);
}
/* Public function; see qcbor_main_decode.h */
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 qcbor_main_decode.h */
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;
}
Done:
return uReturn;
}
/* Public function; see qcbor_main_decode.h */
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);
}
/* Public function; see qcbor_main_decode.h */
void
QCBORDecode_VGetNextConsume(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
{
QCBORDecode_VGetNext(pMe, pDecodedItem);
if(pMe->uLastError == QCBOR_SUCCESS) {
pMe->uLastError = (uint8_t)QCBORDecode_Private_ConsumeItem(pMe, pDecodedItem, NULL,
&pDecodedItem->uNextNestLevel);
}
}
/* Public function; see qcbor_main_decode.h */
QCBORError
QCBORDecode_EndCheck(QCBORDecodeContext *pMe)
{
size_t uCursorOffset;
QCBORError uErr;
uErr = QCBORDecode_GetError(pMe);
if(uErr != QCBOR_SUCCESS) {
return uErr;
}
uCursorOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
if(uCursorOffset == UsefulInputBuf_GetBufferLength(&(pMe->InBuf))) {
return QCBOR_ERR_NO_MORE_ITEMS;
}
return QCBOR_SUCCESS;
}
/**
* @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)
{
QCBORError uErr;
uint8_t uNestLevel;
size_t uStartingCursor;
size_t uStartOfReturned;
size_t uEndOfReturned;
size_t uTempSaveCursor;
bool bInMap;
QCBORItem LabelItem;
bool EndedByBreak;
uStartingCursor = UsefulInputBuf_Tell(&(pMe->InBuf));
bInMap = DecodeNesting_IsCurrentTypeMap(&(pMe->nesting));
uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem, NULL);
if(uErr != QCBOR_SUCCESS) {
pMe->uLastError = (uint8_t)uErr;
return;
}
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 */
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);
/* Item has been fetched once so safe to ignore error */
(void)QCBORDecode_Private_GetNextTagNumber(pMe, &LabelItem);
uStartOfReturned = UsefulInputBuf_Tell(&(pMe->InBuf));
UsefulInputBuf_Seek(&(pMe->InBuf), uTempSaveCursor);
} else {
uStartOfReturned = uStartingCursor;
}
/* 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;
}
/* 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:
return;
}
#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.
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.
The sizes packed in are uint32_t to be the same on all CPU types
and simplify the code.
========================================================================== */
static int
MemPool_Unpack(const void *pMem, uint32_t *puPoolSize, uint32_t *puFreeOffset)
{
// 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);
}
static int
MemPool_Pack(UsefulBuf Pool, uint32_t uFreeOffset)
{
// 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);
}
/*
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
*/
static UsefulBuf
MemPool_Function(void *pPool, void *pMem, size_t uNewSize)
{
UsefulBuf ReturnValue = NULLUsefulBuf;
uint32_t uPoolSize;
uint32_t uFreeOffset;
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;
if(MemPool_Unpack(pPool, &uPoolSize, &uFreeOffset)) {
goto Done;
}
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;
// 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);
// 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;
// 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
}
}
UsefulBuf Pool = {pPool, uPoolSize};
MemPool_Pack(Pool, uFreeOffset);
Done:
return ReturnValue;
}
/* Public function; see qcbor_main_decode.h */
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)
#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;
}
// 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;
}
QCBORDecode_SetUpAllocator(pMe, MemPool_Function, Pool.ptr, bAllStrings);
return QCBOR_SUCCESS;
}
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
/* Public function; see qcbor_main_decode.h */
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 */
}
// Improvement: add methods for wrapped CBOR, a simple alternate
// to EnterBstrWrapped