homogenous arrays starting to work
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 7e52a26..a7c33e0 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -185,6 +185,9 @@
#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 016226b..6ac004e 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -1957,6 +1957,39 @@
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 5c28c7e..a18c3c6 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -2584,6 +2584,122 @@
}
+
+
+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 b3301c7..223cc7f 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -5919,3 +5919,209 @@
}
#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 59c420b..55d51f6 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -950,3 +950,195 @@
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 779b094..3197cf3 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -8003,3 +8003,43 @@
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 54e125a..2980b99 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -314,4 +314,10 @@
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 53d83f6..04f600a 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -137,7 +137,8 @@
TEST_ENTRY(ExponentAndMantissaEncodeTests),
#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
TEST_ENTRY(ParseEmptyMapInMapTest),
- TEST_ENTRY(BoolTest)
+ TEST_ENTRY(BoolTest),
+ TEST_ENTRY(DecodeArraysOfTest)
};