From f6029f8958199d1e1f1d3ace681ff3b2f624599a Mon Sep 17 00:00:00 2001 From: Laurence Lundblade Date: Thu, 3 Jun 2021 00:55:46 -0700 Subject: [PATCH 1/2] homogenous arrays starting to work --- inc/qcbor/qcbor_common.h | 3 + inc/qcbor/qcbor_encode.h | 33 +++++ inc/qcbor/qcbor_spiffy_decode.h | 116 ++++++++++++++++++ src/qcbor_decode.c | 206 ++++++++++++++++++++++++++++++++ src/qcbor_encode.c | 192 +++++++++++++++++++++++++++++ test/qcbor_decode_tests.c | 40 +++++++ test/qcbor_decode_tests.h | 6 + test/run_tests.c | 3 +- 8 files changed, 598 insertions(+), 1 deletion(-) diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h index 7e52a267..a7c33e02 100644 --- a/inc/qcbor/qcbor_common.h +++ b/inc/qcbor/qcbor_common.h @@ -185,6 +185,9 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define CBOR_TAG_MIME 36 /** See QCBOREncode_AddBinaryUUID(). */ #define CBOR_TAG_BIN_UUID 37 +/** An array where all elements are of the same type. See + [RFC8746](https://tools.ietf.org/html/rfc8932) */ +#define CBOR_TAG_HOMOGENEOUS_ARRAY 41 /** The data is a CBOR Web Token per [RFC 8392] (https://tools.ietf.org/html/rfc8932). No API is provided for this tag. */ diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h index 016226bd..6ac004e8 100644 --- a/inc/qcbor/qcbor_encode.h +++ b/inc/qcbor/qcbor_encode.h @@ -1957,6 +1957,39 @@ UsefulBufC QCBOREncode_EncodeHead(UsefulBuf buffer, uint64_t uNumber); +/* +Definitely need to have two functions, one for BE and one for LE. + Since we have to say what format to output. There is no wire format. + + Do we want a third that just outputs without swapping? It would + be called when the caller doesn't want to figure out which + endianness they have. They want the minimal processing. + The implementation will have to figure out which though to +use correct tag. + + + + + + */ + + +void +QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const int64_t *puInts, + size_t uNumInts); + +void QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pCtx, + const uint32_t array[], + size_t uArrayLen); + +void QCBOREncode_AddUint32ArrayLittleEndian(QCBOREncodeContext *pCtx, +const uint32_t array[], +size_t uArrayLen); + + + /* ========================================================================= diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h index 5c28c7e1..a18c3c6c 100644 --- a/inc/qcbor/qcbor_spiffy_decode.h +++ b/inc/qcbor/qcbor_spiffy_decode.h @@ -2584,6 +2584,122 @@ QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pMe, } + + +union QCBORHomogenousArray { + int64_t *pnInt64s; + int64_t *puUInt64s; + double *pDoubles; + UsefulBufC *pStrings; +}; + +void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount); + + +static void inline +QCBORDecode_GetArrayOfInt64(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *pnInt64s, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pnInt64s = pnInt64s; + + QCBORDecode_GetHomogenousArray(pMe, + uTagRequirement, + QCBOR_TYPE_INT64, + nInArrayCount, + array, + pnOutArrayCount); +} + + +static void inline +QCBORDecode_GetArrayOfUInt64(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *puUInt64s, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pnInt64s = puUInt64s; + + QCBORDecode_GetHomogenousArray(pMe, + uTagRequirement, + QCBOR_TYPE_UINT64, + nInArrayCount, + array, + pnOutArrayCount); +} + + +static void inline +QCBORDecode_GetArrayOfTextStrings(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + size_t nInArrayCount, + UsefulBufC *pStrings, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pStrings = pStrings; + + QCBORDecode_GetHomogenousArray(pMe, + uTagRequirement, + QCBOR_TYPE_TEXT_STRING, + nInArrayCount, + array, + pnOutArrayCount); +} + + +static void inline +QCBORDecode_GetArrayOfByteStrings(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + size_t nInArrayCount, + UsefulBufC *pStrings, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pStrings = pStrings; + + QCBORDecode_GetHomogenousArray(pMe, + uTagRequirement, + QCBOR_TYPE_BYTE_STRING, + nInArrayCount, + array, + pnOutArrayCount); +} + + +static void inline +QCBORDecode_GetArrayOfDoubles(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + size_t nInArrayCount, + double *pDoubles, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pDoubles = pDoubles; + + QCBORDecode_GetHomogenousArray(pMe, + uTagRequirement, + QCBOR_TYPE_DOUBLE, + nInArrayCount, + array, + pnOutArrayCount); +} + #ifdef __cplusplus } #endif diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c index b3301c78..223cc7f9 100644 --- a/src/qcbor_decode.c +++ b/src/qcbor_decode.c @@ -5919,3 +5919,209 @@ void QCBORDecode_GetBigFloatBigInMapSZ(QCBORDecodeContext *pMe, } #endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */ + + + +/* The one that matches the endianness should return without copying + The other either has to copy or has to swap by violating const-ness. + If copying, the caller has to know. If violating const-ness, then +the caller only knows that const-ness is violated. */ + +void QCBORDecode_GetUint32ArrayBE(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + uint32_t *pInts, + size_t *pSize) +{ + QCBORItem item; + QCBORDecode_VGetNext(pMe, &item); + + if(item.uDataType != QCBOR_TYPE_BYTE_STRING || + !QCBORDecode_IsTagged(pMe, &item, 99)) { // TODO: correct tag number + pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; + return; + } + + if(item.val.string.len % 4 != 0) { + pMe->uLastError = 99; // TODO: figure out error + return; + } + + *pSize = item.val.string.len / 4; + + /* may have to swap if endianness doesn't match */ + pInts = (uint32_t *) &(item.val.string.ptr); +} + + +void QCBORDecode_GetUint32ArrayLE(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + uint32_t *pInts, + size_t *pSize) +{ + QCBORItem item; + QCBORDecode_GetNext(pMe, &item); + + // TODO: check type + // TODO: check size is a multiple of 4 + + *pSize = item.val.string.len / 4; + + // TODO: must swap all the ints + + pInts = (uint32_t *) &(item.val.string.ptr); + + +} + + + + + +static QCBORError +CheckTagList(uint64_t uTag, const uint64_t *puTagList) +{ + for(size_t i = 0; puTagList[i] != CBOR_TAG_INVALID64; i++) { + if(uTag == puTagList[i]) { + return QCBOR_SUCCESS; + } + } + return QCBOR_ERR_UNEXPECTED_TYPE; +} + + +QCBORError CheckTagRequirement2(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + const uint64_t *puTagNumbers, + const uint8_t *puContentTypes, + const QCBORItem *pItem) +{ + /* In this checker, the tags of interest are never mapped + to QCBOR_TYPEs. */ + + /* Check the content type first. If that is wrong, then this fails + no matter what. */ + QCBORError uReturn = CheckTypeList(pItem->uDataType, puContentTypes); + if(uReturn != QCBOR_SUCCESS) { + return uReturn; + } + + uReturn = CheckTagList(QCBORDecode_GetNthTag(pMe, pItem, 0), puTagNumbers); + + const int nTagReq = uTagRequirement & ~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS; + switch(nTagReq) { + case QCBOR_TAG_REQUIREMENT_TAG: + if(uReturn != QCBOR_SUCCESS) { + goto Done; + } + break; + + case QCBOR_TAG_REQUIREMENT_NOT_A_TAG: + if(uReturn == QCBOR_SUCCESS) { + uReturn = QCBOR_ERR_UNEXPECTED_TYPE; + goto Done; + } + break; + + case QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG: + break; + } + + if(!(uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS)) { + /* Additional tags are prohibited. */ + int nAdditionalIndex = 0; + if(uReturn == QCBOR_SUCCESS) { + nAdditionalIndex++; + } + if(pItem->uTags[0] != CBOR_TAG_INVALID16) { + uReturn = QCBOR_ERR_UNEXPECTED_TYPE; + } + } + uReturn = QCBOR_SUCCESS; + +Done: + return uReturn; +} + + + + +void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount) +{ + QCBORItem item; + QCBORDecode_GetNext(pMe, &item); + + const uint64_t puAllowedTags[] = {CBOR_TAG_HOMOGENEOUS_ARRAY, CBOR_TAG_INVALID64}; + + const uint8_t puAllowedContents[] = {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE}; + + + QCBORError uError = CheckTagRequirement2(pMe, + uTagRequirement, + puAllowedTags, + puAllowedContents, + &item); + if(uError) { + pMe->uLastError = (uint8_t)uError; + goto Done; + } + + const uint8_t uIntNestLevel = item.uNextNestLevel; + + size_t uIntCount = 0; + do { + QCBORDecode_GetNext(pMe, &item); + if(item.uDataType != uType) { + pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; + goto Done; + } + + /* A good compiler should not generate much code here since + since it is just a copy of either 8 or 16 bytes for the + cases. */ + switch (uType) { + case QCBOR_TYPE_INT64: + if(array.pnInt64s) { + array.pnInt64s[uIntCount] = item.val.int64; + } + break; + + case QCBOR_TYPE_UINT64: + if(array.puUInt64s) { + array.puUInt64s[uIntCount] = item.val.int64; + } + break; + + case QCBOR_TYPE_DOUBLE: + if(array.pDoubles) { + array.pDoubles[uIntCount] = item.val.dfnum; + } + break; + + case QCBOR_TYPE_TEXT_STRING: + case QCBOR_TYPE_BYTE_STRING: + if(array.pStrings) { + array.pStrings[uIntCount] = item.val.string; + } + break; + } + uIntCount++; + + if(uIntCount >= nInArrayCount) { + pMe->uLastError = QCBOR_ERR_BUFFER_TOO_SMALL; + goto Done; + } + + } while(item.uNextNestLevel >= uIntNestLevel); + + + *pnOutArrayCount = uIntCount; + +Done: + return; +} + diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c index 59c420b4..55d51f66 100644 --- a/src/qcbor_encode.c +++ b/src/qcbor_encode.c @@ -950,3 +950,195 @@ QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *me, size_t *puEncodedLe return nReturn; } + + + +static inline void UsefulOutBuf_InsertUint32LE(UsefulOutBuf *pMe, + uint32_t uInteger32, + size_t uPos) +{ + /* See UsefulOutBuf_InsertUint64() for comments on this code */ + + /* htonl is not used (but it is used for outputting BE) + + htonl will swap if CPU is little endian and do nothing if it is big endian + + The opposite is needed. + + Could use htonl and then always swap the result, but + that doesn't seem any better than the default + shift and swap that always works. + + + */ + + const void *pBytes; + +#if defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) + pBytes = &uInteger32; + +#elif defined(USEFULBUF_CONFIG_BIG_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP) + uint32_t uTmp = __builtin_bswap32(uInteger32); + + pBytes = &uTmp; + +#else + uint8_t aTmp[4]; + + aTmp[0] = (uint8_t)((uInteger32 & 0xff) >> 24); + aTmp[1] = (uint8_t)((uInteger32 & 0xff00) >> 16); + aTmp[2] = (uint8_t)((uInteger32 & 0xff0000) >> 8); + aTmp[3] = (uint8_t)(uInteger32 & 0xff000000); + + pBytes = aTmp; +#endif + + UsefulOutBuf_InsertData(pMe, pBytes, 4, uPos); +} + +void +QCBOREncode_AddUint32ArrayLittleEndian(QCBOREncodeContext *pMe, + const uint32_t array[], + size_t uArrayLen) +{ + QCBOREncode_AddTag(pMe, 84); // TODO: the correct tag + + const size_t uNumBytes = uArrayLen * sizeof(uint32_t); + + AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uNumBytes, 0); + + for(size_t n = 0; n < uArrayLen; n++) { + UsefulOutBuf_InsertUint32LE(&(pMe->OutBuf), array[n], 99); // TODO: correct position + } +} + +void +QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pMe, + const uint32_t array[], + size_t uArrayLen) +{ + QCBOREncode_AddTag(pMe, 84); // TODO: the correct tag + + const size_t uNumBytes = uArrayLen * sizeof(uint32_t); + + AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uNumBytes, 0); + + for(size_t n = 0; n < uArrayLen; n++) { + UsefulOutBuf_AppendUint32(&(pMe->OutBuf), array[n]); + } +} + + +// Note that this will produce an array of major type 0 and 1, +// but that is what is necessary to have an array of positive +// and negative integers. The types allowed for a homogeneous +// array are caller-defined, not strictly by CBOR major type +// or such, so this is just fine. +void +QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const int64_t *puInts, + size_t uNumInts) +{ + if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { + QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); + } + QCBOREncode_OpenArray(pMe); + + for(size_t i = 0; i < uNumInts; i++) { + QCBOREncode_AddInt64(pMe, puInts[i]); + } + QCBOREncode_CloseArray(pMe); +} + + +void +QCBOREncode_AddArrayOfUInts(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const uint64_t *puInts, + size_t uNumInts) +{ + if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { + QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); + } + QCBOREncode_OpenArray(pMe); + + for(size_t i = 0; i < uNumInts; i++) { + QCBOREncode_AddUInt64(pMe, puInts[i]); + } + QCBOREncode_CloseArray(pMe); +} + + +void +QCBOREncode_AddArrayOfDoubles(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const double *pdDoubles, + size_t uNumDoubles) +{ + if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { + QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); + } + QCBOREncode_OpenArray(pMe); + + for(size_t i = 0; i < uNumDoubles; i++) { + QCBOREncode_AddDouble(pMe, pdDoubles[i]); + } + QCBOREncode_CloseArray(pMe); +} + + +void +QCBOREncode_AddArrayOfByteStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const UsefulBufC *pStrings, + size_t uNumStrings) +{ + if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { + QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); + } + QCBOREncode_OpenArray(pMe); + + for(size_t i = 0; i < uNumStrings; i++) { + QCBOREncode_AddBytes(pMe, pStrings[i]); + } + QCBOREncode_CloseArray(pMe); +} + +void +QCBOREncode_AddArrayOfTextStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const UsefulBufC *pStrings, + size_t uNumStrings) +{ + if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { + QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); + } + QCBOREncode_OpenArray(pMe); + + for(size_t i = 0; i < uNumStrings; i++) { + QCBOREncode_AddText(pMe, pStrings[i]); + } + QCBOREncode_CloseArray(pMe); +} + + +void +QCBOREncode_AddArrayOSZStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const char **pStrings, + size_t uNumStrings) +{ + if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { + QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); + } + QCBOREncode_OpenArray(pMe); + + for(size_t i = 0; i < uNumStrings; i++) { + QCBOREncode_AddSZString(pMe, pStrings[i]); + } + QCBOREncode_CloseArray(pMe); +} + + + diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c index 779b0944..3197cf31 100644 --- a/test/qcbor_decode_tests.c +++ b/test/qcbor_decode_tests.c @@ -8001,5 +8001,45 @@ int32_t BoolTest(void) return 15; } + return 0; +} + + +int32_t DecodeArraysOfTest(void) +{ + int64_t ints[] = {-1, 0, 5000, -9000}; + + QCBOREncodeContext EC; + + MakeUsefulBufOnStack(xx, 100); + QCBOREncode_Init(&EC, xx); + + QCBOREncode_AddArrayOfInts(&EC, + QCBOR_TAG_REQUIREMENT_NOT_A_TAG, + ints, + 4); + UsefulBufC Encoded; + QCBOREncode_Finish(&EC, &Encoded); + + + QCBORDecodeContext DC; + + int64_t i2[6]; + size_t count; + + QCBORDecode_Init(&DC, Encoded, 0); + + QCBORDecode_GetArrayOfInt64(&DC, + QCBOR_TAG_REQUIREMENT_NOT_A_TAG, + 6, + i2, + &count); + + + + + + + return 0; } diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h index 54e125a5..2980b990 100644 --- a/test/qcbor_decode_tests.h +++ b/test/qcbor_decode_tests.h @@ -314,4 +314,10 @@ Test decoding of booleans int32_t BoolTest(void); +/* +Test decoding of arrays of integers, strings... +*/ +int32_t DecodeArraysOfTest(void); + + #endif /* defined(__QCBOR__qcbort_decode_tests__) */ diff --git a/test/run_tests.c b/test/run_tests.c index 53d83f61..04f600ab 100644 --- a/test/run_tests.c +++ b/test/run_tests.c @@ -137,7 +137,8 @@ static test_entry s_tests[] = { TEST_ENTRY(ExponentAndMantissaEncodeTests), #endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */ TEST_ENTRY(ParseEmptyMapInMapTest), - TEST_ENTRY(BoolTest) + TEST_ENTRY(BoolTest), + TEST_ENTRY(DecodeArraysOfTest) }; From f318d1745d225207ca8e2ed4f3a2c99f6e5ada94 Mon Sep 17 00:00:00 2001 From: Laurence Lundblade Date: Mon, 9 Aug 2021 21:10:54 -0700 Subject: [PATCH 2/2] checkpoint work on arrays --- README.md | 102 ++++++++++--- inc/qcbor/UsefulBuf.h | 32 ++-- inc/qcbor/qcbor_common.h | 18 +++ inc/qcbor/qcbor_encode.h | 96 ++++++++++-- inc/qcbor/qcbor_spiffy_decode.h | 263 +++++++++++++++++++++++++++++++- src/qcbor_decode.c | 244 ++++++++++++++++++++++++++--- src/qcbor_encode.c | 126 ++++++++------- test/qcbor_decode_tests.c | 25 ++- 8 files changed, 776 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 7a55e3ef..1cb9a59b 100644 --- a/README.md +++ b/README.md @@ -201,34 +201,94 @@ unlimited. Some of the error codes have changed. -## Floating Point Support +## Floating Point Support & Configuration By default, all QCBOR floating-point features are enabled. This includes encoding and decoding of half-precision, CBOR preferred serialization for floating-point and floating-point epoch dates. If full floating-point is not needed the following #defines can be -used to reduce object code size. +used to reduce object code size and dependency. + +See discussion in qcbor_encode.h for other details. + +### QCBOR_DISABLE_FLOAT_HW_USE + +This removes dependency on: + +* floating-point hardware and floating-point instructions +* and +* the math library (libm, -lm) + +For most limited environments, this removes enough +floating-point dependencies to be able to compile +and run QCBOR. + +Note that this does not remove use of the types +double and float from QCBOR, but it limits QCBOR's +use of them to converting the encoded byte stream to them +and copying them. Converting and copying them +usually doesn't require any hardware, libraries +or includes. The C compiler takes care of it on its +own. + +QCBOR uses it's own implementation of +half-precision float-pointing that doesn't depend on +math libraries. It uses masks and shifts instead. Thus +even with this define, half-precision encoding and +decoding works. + +When this is defined, the QCBOR functionality lost +is minimal and only for decoding: + +* Decoding floating-point format dates are not handled +* There is no conversion between floats and integers when decoding. For +example, QCBORDecode_GetUInt64ConvertAll() will be unable to convert +to and from float-point. +* Floats will not be converted to double when decoding. + +No interfaces are disabled or removed with this define. +If input that requires floating-point conversion or functions +are called that request floating-point conversion, an error +code like QCBOR_ERR_HW_FLOAT_DISABLED will be +returned. + +This saves only a small amount of object code. The primary +purpose for defining this is to remove dependency on +floating point hardware and libraries. + +### QCBOR_DISABLE_PREFERRED_FLOAT + +This eliminates support for half-precision +and CBOR preferred serialization by disabling +QCBOR's shift and mask based implementation of +half-precision floating-point. + +With this defined, single and double-precision floating-point +numbers can still be encoded and decoded. Conversion +of floating-point to and from integers, big numbers and +such is also supported. Floating-point dates are still +supported. + +The primary reason to define this is to save object code. +Roughly 900 bytes are saved, though about half of this +can be saved just by not calling any functions that +encode floating point numbers. + +### Compiler options + +Compilers support a number of options that control +which float-point related code is generated. For example, +it is usually possible to give options to the compiler to avoid all +floating-point hardware and instructions, to use software +and replacement libraries instead. These are usually +bigger and slower, but these options may still be useful +in getting QCBOR to run in some environments in +combination with QCBOR_DISABLE_FLOAT_HW_USE. +In particular, -mfloat-abi=soft, disables use of + hardware instructions for the float and double + types in C for some architectures. -QCBOR_DISABLE_FLOAT_HW_USE -- Avoid all use of floating-point hardware -and inclusion of and - -QCBOR_DISABLE_PREFERRED_FLOAT -- Eliminates support for half-precision -and CBOR preferred serialization. - -Even if these are #defined, QCBOR can still encode and decode basic -floating point numbers. - -Defining QCBOR_DISABLE_PREFERRED_FLOAT will reduce object code size by -about 900 bytes, though 550 of these bytes can be avoided without the -#define by just not calling any of the functions that encode -floating-point numbers. - -Defining QCBOR_DISABLE_FLOAT_HW_USE will save a small amount of object -code. Its main use is on CPUs that have no floating-point hardware. - -See discussion in qcbor_encode.h for details. -G ## Comparison to TinyCBOR TinyCBOR is a popular widely used implementation. Like QCBOR, diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h index 990191af..2974adfc 100644 --- a/inc/qcbor/UsefulBuf.h +++ b/inc/qcbor/UsefulBuf.h @@ -1580,6 +1580,18 @@ static inline size_t UsefulInputBuf_GetBufferLength(UsefulInputBuf *pUInBuf); static void UsefulInputBuf_SetBufferLength(UsefulInputBuf *pUInBuf, size_t uNewLen); +// TODO: document this; maybe add tests... +#if defined(USEFULBUF_CONFIG_BSWAP) +#define USEFUL_SWAP32(integer) \ + __builtin_bswap32(integer); +#else +#define USEFUL_SWAP32(integer) \ + ((integer & 0xff) >> 24) + \ + ((integer & 0xff00) >> 16) + \ + ((integer & 0xff0000) >> 8) + \ + (integer & 0xff000000) +#endif + /*---------------------------------------------------------- @@ -1789,6 +1801,7 @@ static inline void UsefulOutBuf_InsertByte(UsefulOutBuf *me, } + static inline void UsefulOutBuf_InsertUint16(UsefulOutBuf *me, uint16_t uInteger16, size_t uPos) @@ -1836,20 +1849,21 @@ static inline void UsefulOutBuf_InsertUint32(UsefulOutBuf *pMe, uint32_t uTmp = htonl(uInteger32); pBytes = &uTmp; -#elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP) - uint32_t uTmp = __builtin_bswap32(uInteger32); - +#elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) + uint32_t uTmp = XSWAP(uInteger32); pBytes = &uTmp; #else - uint8_t aTmp[4]; - aTmp[0] = (uint8_t)((uInteger32 & 0xff000000) >> 24); - aTmp[1] = (uint8_t)((uInteger32 & 0xff0000) >> 16); - aTmp[2] = (uint8_t)((uInteger32 & 0xff00) >> 8); - aTmp[3] = (uint8_t)(uInteger32 & 0xff); + uint32_t uTmp = + ((uInteger32 & 0xff) >> 24) + \ + ((uInteger32 & 0xff00) >> 16) + \ + ((uInteger32 & 0xff0000) >> 8) + \ + (uInteger32 & 0xff000000); + + pBytes = &uTmp; + - pBytes = aTmp; #endif UsefulOutBuf_InsertData(pMe, pBytes, 4, uPos); diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h index a7c33e02..b1a2e96d 100644 --- a/inc/qcbor/qcbor_common.h +++ b/inc/qcbor/qcbor_common.h @@ -197,6 +197,16 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. tag. */ #define CBOR_TAG_CBOR_SEQUENCE 63 +#define CBOR_TAG_UINT8_ARRAY 64 + +#define CBOR_TAG_UINT16_BIG_ENDIAN_ARRAY 65 +#define CBOR_TAG_UINT32_BIG_ENDIAN_ARRAY 66 +#define CBOR_TAG_UINT64_BIG_ENDIAN_ARRAY 67 + +#define CBOR_TAG_UINT16_LITTLE_ENDIAN_ARRAY 69 +#define CBOR_TAG_UINT32_LITTLE_ENDIAN_ARRAY 70 +#define CBOR_TAG_UINT64_LITTLE_ENDIAN_ARRAY 71 + #define CBOR_TAG_ENCRYPT 96 /** Not Decoded by QCBOR. Tag for COSE format MAC. See [RFC 8152, COSE] @@ -505,6 +515,14 @@ typedef enum { indefinite length map or array in the input CBOR. */ QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED = 44, + /** The input CBOR needs to be a multiple of 2, 4 or 8 bytes and it is not. */ + QCBOR_ERR_INPUT_SIZE_MULTIPLE = 45, + + /** The input CBOR is not a tag indicating endianness and the input endianness + needs to be know to proceed. */ + QCBOR_ERR_INPUT_ENDIANNESS_UNKNOWN = 45, + + /* This is stored in uint8_t; never add values > 255 */ } QCBORError; diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h index 6ac004e8..c25fbc54 100644 --- a/inc/qcbor/qcbor_encode.h +++ b/inc/qcbor/qcbor_encode.h @@ -503,9 +503,9 @@ void QCBOREncode_Init(QCBOREncodeContext *pCtx, UsefulBuf Storage); */ void QCBOREncode_AddInt64(QCBOREncodeContext *pCtx, int64_t nNum); -static void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t uNum); +static void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t nNum); -static void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t uNum); +static void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t nNum); /** @@ -1957,6 +1957,79 @@ UsefulBufC QCBOREncode_EncodeHead(UsefulBuf buffer, uint64_t uNumber); + + + +/** + @brief Encode a "homogeneous" array of signed integers. + + @param[in] pCtx The encoding context. + @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or + @ref QCBOR_ENCODE_AS_BORROWED. + @param[in] pnNum The array of integers to output. + @param[in] uNumInts The number of integers in \c pnNum. + + This encodes a homogeneous array of signed integers as described in section 3.2 in [RFC 8746](https://tools.ietf.org/html/rfc8746). + + To encode the actual integers, this just opens an array, adds all the integers + and closes the array as one might expect. The only thing unusual is the + encoding of this as a tag. + + If \c uTagRequirement is @ref QCBOR_ENCODE_AS_TAG then + this is encoded as the homogenous array tag, which means the array + is preceded by the tag number \ref CBOR_TAG_HOMOGENEOUS_ARRAY. + + If \c uTagRequirement is @ref QCBOR_ENCODE_AS_BORROWED then + there is no tag number and this is the straight forward encoding of an + array of integers. This is "borrowed" because the definition of a homogenous + array is as a tag. + + This uses QCBOREncode_AddInt64() to encode the integers so + preferred encoding in the shortest form is used and this will encode + with either major type 0 or 1 depending on whether each integer is + positive or negative. +*/ +void +QCBOREncode_AddArrayOfInt64s(QCBOREncodeContext *pCtx, + uint8_t uTagRequirement, + const int64_t pnNum[], + size_t uNumInts); + + +void +QCBOREncode_AddArrayOfUInt64s(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const uint64_t puInts[], + size_t uNumUInts); + + +void +QCBOREncode_AddArrayOfDoubles(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const double pdDoubles[], + size_t uNumDoubles); + + +void +QCBOREncode_AddArrayOfByteStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const UsefulBufC pStrings[], + size_t uNumStrings); + + +void +QCBOREncode_AddArrayOfTextStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const UsefulBufC pStrings[], + size_t uNumStrings); + + +void +QCBOREncode_AddArrayOfSZStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const char *pStrings[], + size_t uNumStrings); + /* Definitely need to have two functions, one for BE and one for LE. Since we have to say what format to output. There is no wire format. @@ -1968,19 +2041,16 @@ Definitely need to have two functions, one for BE and one for LE. use correct tag. - +There are going to be about 20 functions here for + all the different integer types. */ -void -QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe, - uint8_t uTagRequirement, - const int64_t *puInts, - size_t uNumInts); -void QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pCtx, + +void QCBOREncode_AddTypedArrayOfUInt32BigEndian(QCBOREncodeContext *pCtx, const uint32_t array[], size_t uArrayLen); @@ -2156,18 +2226,18 @@ static inline void QCBOREncode_AddBytesLenOnlyToMapN(QCBOREncodeContext *pCtx, i static inline void -QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, int64_t uNum) +QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, int64_t nNum) { // Use _AddBuffer() because _AddSZString() is defined below, not above QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel)); - QCBOREncode_AddInt64(pMe, uNum); + QCBOREncode_AddInt64(pMe, nNum); } static inline void -QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, int64_t uNum) +QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, int64_t nNum) { QCBOREncode_AddInt64(pMe, nLabel); - QCBOREncode_AddInt64(pMe, uNum); + QCBOREncode_AddInt64(pMe, nNum); } diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h index a18c3c6c..424b5aea 100644 --- a/inc/qcbor/qcbor_spiffy_decode.h +++ b/inc/qcbor/qcbor_spiffy_decode.h @@ -1700,6 +1700,176 @@ void QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pCtx); +/** + @brief Get a "homgeneous" array of 64-bit integers + + @param[in] pCtx The decode context. + + This decodes was is called the "homogenous array" tag in RFC xxxx. + The input must be a normal CBOR array where every item + in the array is CBOR item of major type 0 or 1. + + See also QCBORDecode_GetTypedArrayOfInt64s() for an alternate encoding of 64-bit integers that is + more compact in some cases, but is NOT endian neutral. + +*/ +static void +QCBORDecode_GetArrayOfInt64(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *pnInt64s, + size_t *pnOutArrayCount); + +static void +QCBORDecode_GetArrayOfInt64InMapN(QCBORDecodeContext *pCtx, + int64_t nLabel, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *pnInt64s, + size_t *pnOutArrayCount); + +static void +QCBORDecode_GetArrayOfInt64InMapSZ(QCBORDecodeContext *pCtx, + const char *szLabel, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *pnInt64s, + size_t *pnOutArrayCount); + + + +static void inline +QCBORDecode_GetArrayOfUInt64(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *puUInt64s, + size_t *pnOutArrayCount); + + +static void inline +QCBORDecode_GetArrayOfTextStrings(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + size_t nInArrayCount, + UsefulBufC *pStrings, + size_t *pnOutArrayCount); + +static void inline +QCBORDecode_GetArrayOfByteStrings(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + size_t nInArrayCount, + UsefulBufC *pStrings, + size_t *pnOutArrayCount); + + +static void inline +QCBORDecode_GetArrayOfDoubles(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + size_t nInArrayCount, + double *pDoubles, + size_t *pnOutArrayCount); + + + +#define IS_BIG_ENDIAN 0 +#define IS_LITTLE_ENDIAN 1 +#define ENDIAN_UNKNOWN 2 + +/** + @brief Get a "typed" array of 64-bit integers + + @param[in] pCtx The decode context. + + This decodes was is called the "type array" tag in RFC xxxx. + The CBOR to be decoded is a byte string where every 8 bytes are interpreted as + as signed 64-bit integer. + + Unlike everything else in CBOR, the integers are NOT in network byte + order. The sender may have encoded them as big or little endian. The point + of doing this is to be able to more-efficiently send large arrays of integers + between implementing end-points that need and support this. This + avoids any copying or byte swapping of the integers saving both + memory and CPU cycles. + + The array may be a CBOR tag or not. \c uTagRequrement instructs the + decoder as to whether it will decode as a tag. + + If encoded CBOR is a tag, it will be known whether what was sent + was big or little endian and returned in \c uEndianness. If it + is not a tag, then the knowledge of the endianess has to come from + else where. Perhaps it is part of the particular protocol that all + type arrays are to be of a particular endianness. +*/ +void QCBORDecode_GetTypedArrayOfInt64s(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + uint64_t **pInts, + size_t *pSize, + uint8_t *uEndianess); // TODO: constants for endianness +/** + * This specifies the enianness processing that should be done for a few + * methods for array decoding that are configurable this way. + * + * Note that the endianness of the input must be known for some + * of these to work. Somtimes the input CBOR is not a tag + * and the endianness is not know. + */ +typedef enum { + /** The output is to be big endian. The input must be a tag indicating its endianness for this to work. */ + QCBOR_ENDIAN_BIG_ENDIAN = 0, + /** The output is to be little endian. The input must be a tag indicating its endianness for this to work. */ + QCBOR_ENDIAN_LITTLE_ENDIAN = 1, + /** A swap will always be performed. It is up to the caller to know the input endianness. */ + QCBOR_ENDIAN_SWAP = 2, + /** A swap will never be performed. It is up to the caller to know the input endianness. */ + QCBOR_ENDIAN_NO_SWAP = 3, + /** Swapping will be performed if necessary to make the output the correct endianness. The input must be a tag indicating its endianness for this to work. */ + QCBOR_ENDIAN_MATCH_ENDIANNESS = 4, +} QCBOREndianRequirement; + + + +/** + @brief Get an "typed" array of 64-bit integers with copying and swapping + + @param[in] pCtx The decode context. + +This decodes the same data type as QCBORDecode_GetTypedArrayOfInt64s(). + Additionaly, it copies the array to a supplied buffer and does byte swapping + for endianness as instructed. + + The received array may or may not have indication of its endianness. + + + + The possible swapping options are: + - big endian; the input must have indication of its endianness for this to work + - little endianess + - swap; the endianness will always be swapped regardless of what of the input is marked + - no swap; there will be no swapping regardless of how the input is marked + - match configured endianness of machine +*/ +void QCBORDecode_GetTypedArrayOfInt64sCopy(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + QCBOREndianRequirement uSwapRequirement, + size_t uSize, + uint64_t **pInts, + size_t *pSize); + + + +void QCBORDecode_GetUInt32Array(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + uint32_t **pInts, + size_t *pSize); + + +void QCBORDecode_GetUint32ArrayCopy(QCBORDecodeContext *pCtx, + uint8_t uTagRequirement, + QCBOREndianRequirement uSwapRequirement, + size_t uSize, + uint32_t *pInts, + size_t *pSize); + + /* =========================================================================== BEGINNING OF PRIVATE INLINE IMPLEMENTATION ========================================================================== */ @@ -2585,7 +2755,10 @@ QCBORDecode_GetBinaryUUIDInMapSZ(QCBORDecodeContext *pMe, - +/** + * Semi-private data structure to hold the pointer into which to copy + * a decoded homogeneous array. + */ union QCBORHomogenousArray { int64_t *pnInt64s; int64_t *puUInt64s; @@ -2593,7 +2766,33 @@ union QCBORHomogenousArray { UsefulBufC *pStrings; }; -void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, +/** + * Semi-private method to decode a homogeneous array + * + * @param[in] pCtx The decode context. + * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX + * @param[in] uType The QCBOR type of the array elements. + * @param[in] nInArrayCount The size in number of elements of @c array passed in. + * @param[in,out] array The buffer into which the array is decoded. + * @param[out] pnOutArrayCount The number of elements in @c array that were filled in. + * + * This decodes the next item as a homogenous array where all the elements in the + * array can be decoded as the QCBOR_TYPE passed in as @c uType. The type + * must be one of @ref QCBOR_TYPE_INT64, @ref QCBOR_TYPE_UINT64, + * @ref QCBOR_TYPE_DOUBLE, @ref QCBOR_TYPE_TEXT_STRING or + * @ref QCBOR_TYPE_BYTE_STRING. Note that for QCBOR_TYPE_INT64 + * the members in the array can be of either major type 0 or 1. + * + * The buffer into which the array is decoded is in the @ref QCBORHomogenousArray + * union. The size of the buffer must be @c nInArrayCount times the size of the + * data type. The number of elements decoded is returned in @c pnOutArrayCount. + * + * @c array may be NULL in which case the array will be decoded and + * @c pnOutArrayCount will be filled in, but note that input will be consumed. + * To actually get the decoded array values QCBORDecode_Rewind() will have to + * be used and it goes back to the start of an entered enclosing array or map. + */ +void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pCtx, uint8_t uTagRequirement, uint8_t uType, size_t nInArrayCount, @@ -2601,6 +2800,22 @@ void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, size_t *pnOutArrayCount); +void QCBORDecode_GetHomogenousArrayInMapN(QCBORDecodeContext *pMe, + int64_t nLabel, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount); + +void QCBORDecode_GetHomogenousArrayInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount); + static void inline QCBORDecode_GetArrayOfInt64(QCBORDecodeContext *pMe, uint8_t uTagRequirement, @@ -2621,6 +2836,50 @@ QCBORDecode_GetArrayOfInt64(QCBORDecodeContext *pMe, } +static void inline +QCBORDecode_GetArrayOfInt64InMapN(QCBORDecodeContext *pMe, + int64_t nLabel, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *pnInt64s, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pnInt64s = pnInt64s; + + QCBORDecode_GetHomogenousArrayInMapN(pMe, + nLabel, + uTagRequirement, + QCBOR_TYPE_INT64, + nInArrayCount, + array, + pnOutArrayCount); +} + +static void inline +QCBORDecode_GetArrayOfInt64InMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + size_t nInArrayCount, + int64_t *pnInt64s, + size_t *pnOutArrayCount) +{ + union QCBORHomogenousArray array; + + array.pnInt64s = pnInt64s; + + QCBORDecode_GetHomogenousArrayInMapSZ(pMe, + szLabel, + uTagRequirement, + QCBOR_TYPE_INT64, + nInArrayCount, + array, + pnOutArrayCount); +} + + + static void inline QCBORDecode_GetArrayOfUInt64(QCBORDecodeContext *pMe, uint8_t uTagRequirement, diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c index 223cc7f9..d7b3958f 100644 --- a/src/qcbor_decode.c +++ b/src/qcbor_decode.c @@ -5922,34 +5922,145 @@ void QCBORDecode_GetBigFloatBigInMapSZ(QCBORDecodeContext *pMe, -/* The one that matches the endianness should return without copying - The other either has to copy or has to swap by violating const-ness. - If copying, the caller has to know. If violating const-ness, then -the caller only knows that const-ness is violated. */ -void QCBORDecode_GetUint32ArrayBE(QCBORDecodeContext *pMe, + + +typedef enum { + QCBOR_IS_BIG_ENDIAN = 0, + QCBOR_IS_LITTLE_ENDIAN = 1, + QCBOR_UNKNOWN_ENDIAN = 2 +} QCBOREndianness; + +void QCBORDecode_GetUint32Array(QCBORDecodeContext *pMe, uint8_t uTagRequirement, - uint32_t *pInts, - size_t *pSize) + uint32_t **puUIntsDecoded, + size_t *puSizeDecoded, + QCBOREndianness *puEndianness) { - QCBORItem item; - QCBORDecode_VGetNext(pMe, &item); + QCBORItem Item; + QCBORDecode_VGetNext(pMe, &Item); - if(item.uDataType != QCBOR_TYPE_BYTE_STRING || - !QCBORDecode_IsTagged(pMe, &item, 99)) { // TODO: correct tag number + if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) { pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE; return; } - if(item.val.string.len % 4 != 0) { - pMe->uLastError = 99; // TODO: figure out error + const TagSpecification TagSpec = + { + uTagRequirement, + {CBOR_TAG_UINT32_BIG_ENDIAN_ARRAY, CBOR_TAG_UINT32_LITTLE_ENDIAN_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}, + {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE} + }; + + pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, &Item); + if(pMe->uLastError) { + return; + } + + if(QCBORDecode_IsTagged(pMe, &Item, CBOR_TAG_UINT32_BIG_ENDIAN_ARRAY)) { + *puEndianness = QCBOR_IS_BIG_ENDIAN; + } else if(QCBORDecode_IsTagged(pMe, &Item, CBOR_TAG_UINT32_LITTLE_ENDIAN_ARRAY)) { + *puEndianness = QCBOR_IS_LITTLE_ENDIAN; + } else { + *puEndianness = QCBOR_UNKNOWN_ENDIAN; + } + + + if(Item.val.string.len % sizeof(uint32_t) != 0) { + pMe->uLastError = QCBOR_ERR_INPUT_SIZE_MULTIPLE; return; } - *pSize = item.val.string.len / 4; + *puSizeDecoded = Item.val.string.len / sizeof(uint32_t); - /* may have to swap if endianness doesn't match */ - pInts = (uint32_t *) &(item.val.string.ptr); + *puUIntsDecoded = (uint32_t *)(Item.val.string.ptr); +} + + + + + +// positive values are QCBOR error codes +#define PERFORM_SWAP -1 +#define DO_NOT_SWAP 0 +static int +ShallWeSwap(QCBOREndianRequirement uSwapRequirement, QCBOREndianness uEndianness) +{ + /* Asked to always swap */ + if(uSwapRequirement == QCBOR_ENDIAN_SWAP) { + return PERFORM_SWAP; + } + + /* Asked to never swap */ + if(uSwapRequirement == QCBOR_ENDIAN_NO_SWAP) { + return DO_NOT_SWAP; + } + + /* The following use cases require the input endianness to be known */ + if(uEndianness == QCBOR_UNKNOWN_ENDIAN) { + return QCBOR_ERR_INPUT_ENDIANNESS_UNKNOWN; + } + + QCBOREndianRequirement uOutput = uSwapRequirement; + if(uOutput == QCBOR_ENDIAN_MATCH_ENDIANNESS) { +#if defined(USEFULBUF_CONFIG_BIG_ENDIAN) + uOutput = QCBOR_ENDIAN_BIG_ENDIAN; +#else + uOutput = QCBOR_ENDIAN_LITTLE_ENDIAN; +#endif + } + + if((QCBOREndianness)uOutput != uEndianness) { + return PERFORM_SWAP; + } else { + return DO_NOT_SWAP; + } +} + + +void QCBORDecode_GetUint32ArrayCopy(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + QCBOREndianRequirement uSwapRequirement, + size_t uBufferSize, + uint32_t *puUIntsBuffer, + size_t *puReturnedSize) +{ + size_t uReceivedArraySize; + uint32_t *puReceivedArray; + QCBOREndianness uEndianness; + + QCBORDecode_GetUint32Array(pMe, + uTagRequirement, + &puReceivedArray, + &uReceivedArraySize, + &uEndianness); + if(pMe->uLastError) { + return; + } + + + if(uBufferSize < uReceivedArraySize) { + /* Given buffer is too small */ + pMe->uLastError = QCBOR_ERR_BUFFER_TOO_SMALL; + return; + } + + memcpy(puUIntsBuffer, puReceivedArray, uReceivedArraySize * sizeof(uint32_t)); + + int nSwapRequest = ShallWeSwap(uSwapRequirement, uEndianness); + + if(nSwapRequest > 0) { + /* Error figuring out whether to swap or not. */ + pMe->uLastError = (uint8_t)nSwapRequest; + return; + } + + if(nSwapRequest == PERFORM_SWAP) { + for(size_t i = 0; i < uReceivedArraySize; i++) { + const uint32_t uArrayElement = USEFUL_SWAP32(puUIntsBuffer[i]); + puUIntsBuffer[i] = uArrayElement; + } + } } @@ -5966,7 +6077,15 @@ void QCBORDecode_GetUint32ArrayLE(QCBORDecodeContext *pMe, *pSize = item.val.string.len / 4; - // TODO: must swap all the ints +#if defined(USEFULBUF_CONFIG_BIG_ENDIAN) + /* Must swap all the unsigned integers */ + uint32_t *pUIntStart = item.val.string.ptr; + uint32_t *pUIntEnd = pUIntStart + *pSize; + for(uint32_t *pUInt = pUIntStart; pUInt < pUIntEnd; pUInt++) { + const uint32_t u = USEFUL_SWAP32(*pUInt); + *pUInt = u; + } +#endif pInts = (uint32_t *) &(item.val.string.ptr); @@ -6043,18 +6162,14 @@ QCBORError CheckTagRequirement2(QCBORDecodeContext *pMe, } - - -void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, +void ProcessHomogenousArray(QCBORDecodeContext *pMe, + const QCBORItem *pItem, uint8_t uTagRequirement, uint8_t uType, size_t nInArrayCount, union QCBORHomogenousArray array, size_t *pnOutArrayCount) { - QCBORItem item; - QCBORDecode_GetNext(pMe, &item); - const uint64_t puAllowedTags[] = {CBOR_TAG_HOMOGENEOUS_ARRAY, CBOR_TAG_INVALID64}; const uint8_t puAllowedContents[] = {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE}; @@ -6064,13 +6179,15 @@ void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, uTagRequirement, puAllowedTags, puAllowedContents, - &item); + pItem); if(uError) { pMe->uLastError = (uint8_t)uError; goto Done; } - const uint8_t uIntNestLevel = item.uNextNestLevel; + const uint8_t uIntNestLevel = pItem->uNextNestLevel; + QCBORItem item; + size_t uIntCount = 0; do { @@ -6125,3 +6242,80 @@ void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, return; } + +void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount) +{ + QCBORItem item; + QCBORDecode_VGetNext(pMe, &item); + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + ProcessHomogenousArray(pMe, + &item, + uTagRequirement, + uType, + nInArrayCount, + array, + pnOutArrayCount); +} + +void QCBORDecode_GetHomogenousArrayInMapN(QCBORDecodeContext *pMe, + int64_t nLabel, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + QCBORItem Item; + QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item); + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + ProcessHomogenousArray(pMe, + &Item, + uTagRequirement, + uType, + nInArrayCount, + array, + pnOutArrayCount); +} + + +void QCBORDecode_GetHomogenousArrayInMapSZ(QCBORDecodeContext *pMe, + const char *szLabel, + uint8_t uTagRequirement, + uint8_t uType, + size_t nInArrayCount, + union QCBORHomogenousArray array, + size_t *pnOutArrayCount) +{ + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + QCBORItem Item; + QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item); + if(pMe->uLastError != QCBOR_SUCCESS) { + return; + } + + ProcessHomogenousArray(pMe, + &Item, + uTagRequirement, + uType, + nInArrayCount, + array, + pnOutArrayCount); +} diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c index 55d51f66..2a3ae31c 100644 --- a/src/qcbor_encode.c +++ b/src/qcbor_encode.c @@ -953,49 +953,43 @@ QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *me, size_t *puEncodedLe -static inline void UsefulOutBuf_InsertUint32LE(UsefulOutBuf *pMe, - uint32_t uInteger32, - size_t uPos) + +static inline void +UsefulOutBuf_InsertUint32LittleEndian(UsefulOutBuf *pMe, + uint32_t uInteger32, + size_t uPos) { /* See UsefulOutBuf_InsertUint64() for comments on this code */ - /* htonl is not used (but it is used for outputting BE) - - htonl will swap if CPU is little endian and do nothing if it is big endian - - The opposite is needed. - - Could use htonl and then always swap the result, but - that doesn't seem any better than the default - shift and swap that always works. - +#if defined(USEFULBUF_CONFIG_BIG_ENDIAN) + const uint32_t uOut = USEFUL_SWAP32(uInteger32); - */ +#else /* Default is little endian; tests catch mis-configuration. */ + const uint32_t uOut = uInteger32; - const void *pBytes; +#endif /* big or little endian */ -#if defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) - pBytes = &uInteger32; - -#elif defined(USEFULBUF_CONFIG_BIG_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP) - uint32_t uTmp = __builtin_bswap32(uInteger32); - - pBytes = &uTmp; + UsefulOutBuf_InsertData(pMe, &uOut, 4, uPos); +} -#else - uint8_t aTmp[4]; +static inline void +UsefulOutBuf_InsertUint32BE(UsefulOutBuf *pMe, + uint32_t uInteger32, + size_t uPos) +{ + /* See UsefulOutBuf_InsertUint64() for comments on this code */ - aTmp[0] = (uint8_t)((uInteger32 & 0xff) >> 24); - aTmp[1] = (uint8_t)((uInteger32 & 0xff00) >> 16); - aTmp[2] = (uint8_t)((uInteger32 & 0xff0000) >> 8); - aTmp[3] = (uint8_t)(uInteger32 & 0xff000000); +#if !defined(USEFULBUF_CONFIG_BIG_ENDIAN) + const uint32_t uOut = USEFUL_SWAP32(uInteger32); - pBytes = aTmp; -#endif +#else /* Default is little endian; tests catch mis-configuration. */ + const uint32_t uOut = uInteger32; +#endif /* big or little endian */ - UsefulOutBuf_InsertData(pMe, pBytes, 4, uPos); + UsefulOutBuf_InsertData(pMe, &uOut, 4, uPos); } + void QCBOREncode_AddUint32ArrayLittleEndian(QCBOREncodeContext *pMe, const uint32_t array[], @@ -1008,12 +1002,13 @@ QCBOREncode_AddUint32ArrayLittleEndian(QCBOREncodeContext *pMe, AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uNumBytes, 0); for(size_t n = 0; n < uArrayLen; n++) { - UsefulOutBuf_InsertUint32LE(&(pMe->OutBuf), array[n], 99); // TODO: correct position + UsefulOutBuf_InsertUint32LittleEndian(&(pMe->OutBuf), array[n], 99); // TODO: correct position } } + void -QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pMe, +QCBOREncode_AddTypedArrayOfUInt32BigEndian(QCBOREncodeContext *pMe, const uint32_t array[], size_t uArrayLen) { @@ -1024,21 +1019,21 @@ QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pMe, AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uNumBytes, 0); for(size_t n = 0; n < uArrayLen; n++) { - UsefulOutBuf_AppendUint32(&(pMe->OutBuf), array[n]); + UsefulOutBuf_InsertUint32BE(&(pMe->OutBuf), array[n], 99); } } -// Note that this will produce an array of major type 0 and 1, -// but that is what is necessary to have an array of positive -// and negative integers. The types allowed for a homogeneous -// array are caller-defined, not strictly by CBOR major type -// or such, so this is just fine. + + +/* + * Public function to encode an array. See qcbor/qcbor_encode.h + */ void -QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe, - uint8_t uTagRequirement, - const int64_t *puInts, - size_t uNumInts) +QCBOREncode_AddArrayOfInt64s(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const int64_t pInts[], + size_t uNumInts) { if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); @@ -1046,34 +1041,40 @@ QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe, QCBOREncode_OpenArray(pMe); for(size_t i = 0; i < uNumInts; i++) { - QCBOREncode_AddInt64(pMe, puInts[i]); + QCBOREncode_AddInt64(pMe, pInts[i]); } QCBOREncode_CloseArray(pMe); } +/* + * Public function to encode an array. See qcbor/qcbor_encode.h + */ void -QCBOREncode_AddArrayOfUInts(QCBOREncodeContext *pMe, - uint8_t uTagRequirement, - const uint64_t *puInts, - size_t uNumInts) +QCBOREncode_AddArrayOfUInt64s(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const uint64_t puInts[], + size_t uNumUInts) { if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); } QCBOREncode_OpenArray(pMe); - for(size_t i = 0; i < uNumInts; i++) { + for(size_t i = 0; i < uNumUInts; i++) { QCBOREncode_AddUInt64(pMe, puInts[i]); } QCBOREncode_CloseArray(pMe); } +/* + * Public function to encode an array. See qcbor/qcbor_encode.h + */ void QCBOREncode_AddArrayOfDoubles(QCBOREncodeContext *pMe, uint8_t uTagRequirement, - const double *pdDoubles, + const double pdDoubles[], size_t uNumDoubles) { if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { @@ -1088,10 +1089,13 @@ QCBOREncode_AddArrayOfDoubles(QCBOREncodeContext *pMe, } +/* + * Public function to encode an array. See qcbor/qcbor_encode.h + */ void QCBOREncode_AddArrayOfByteStrings(QCBOREncodeContext *pMe, uint8_t uTagRequirement, - const UsefulBufC *pStrings, + const UsefulBufC pStrings[], size_t uNumStrings) { if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { @@ -1105,10 +1109,14 @@ QCBOREncode_AddArrayOfByteStrings(QCBOREncodeContext *pMe, QCBOREncode_CloseArray(pMe); } + +/* + * Public function to encode an array. See qcbor/qcbor_encode.h + */ void QCBOREncode_AddArrayOfTextStrings(QCBOREncodeContext *pMe, uint8_t uTagRequirement, - const UsefulBufC *pStrings, + const UsefulBufC pStrings[], size_t uNumStrings) { if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { @@ -1123,11 +1131,14 @@ QCBOREncode_AddArrayOfTextStrings(QCBOREncodeContext *pMe, } +/* + * Public function to encode an array. See qcbor/qcbor_encode.h + */ void -QCBOREncode_AddArrayOSZStrings(QCBOREncodeContext *pMe, - uint8_t uTagRequirement, - const char **pStrings, - size_t uNumStrings) +QCBOREncode_AddArrayOfSZStrings(QCBOREncodeContext *pMe, + uint8_t uTagRequirement, + const char *pStrings[], + size_t uNumStrings) { if(uTagRequirement == QCBOR_ENCODE_AS_TAG) { QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY); @@ -1139,6 +1150,3 @@ QCBOREncode_AddArrayOSZStrings(QCBOREncodeContext *pMe, } QCBOREncode_CloseArray(pMe); } - - - diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c index 3197cf31..9423b48b 100644 --- a/test/qcbor_decode_tests.c +++ b/test/qcbor_decode_tests.c @@ -8014,7 +8014,7 @@ int32_t DecodeArraysOfTest(void) MakeUsefulBufOnStack(xx, 100); QCBOREncode_Init(&EC, xx); - QCBOREncode_AddArrayOfInts(&EC, + QCBOREncode_AddArrayOfInt64s(&EC, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, ints, 4); @@ -8022,11 +8022,34 @@ int32_t DecodeArraysOfTest(void) QCBOREncode_Finish(&EC, &Encoded); +/* +get an array of ints, + uint, + double, + text strings + and byte strings + get an array of ints where one is a uint + get an array of uints where one is negative + + get an array of ints where the buffer is too small + + get an array of ints and require a tag; input is a tag + get an array of ints and require no tag; input is a tag + get an array of ints and requiare a tag; intput isn't a tag + get an array of ints and tag is optional; intput is a tag + get an array of ints and tag is optional; input is not a tag + + + + */ + + QCBORDecodeContext DC; int64_t i2[6]; size_t count; + QCBORDecode_Init(&DC, Encoded, 0); QCBORDecode_GetArrayOfInt64(&DC,