Map sorting
* Checkpoint work on sorting and CDE
* sort seems to be working
* Complete testing and doc
* revert float stuff so this PR is only sorting
* Documentation and tidiness for UsefulOutBuf
* Final tidy-up of sorting doc and code
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index c1e6cd7..2254951 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -274,8 +274,8 @@
E776E08C214AE07400E67947 /* qcbor_encode.c */,
E776E08E214AE07500E67947 /* qcbor_decode.c */,
E776E08D214AE07500E67947 /* UsefulBuf.c */,
- E73B57582161CA690080D658 /* ieee754.c */,
E73B57572161CA680080D658 /* ieee754.h */,
+ E73B57582161CA690080D658 /* ieee754.c */,
E7864765252CE63100A0C11B /* qcbor_err_to_str.c */,
);
name = src;
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index aa24507..94688db 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -1,6 +1,6 @@
/*============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2022, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Copyright (c) 2021, Arm Limited. All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -42,6 +42,9 @@
when who what, where, why
-------- ---- --------------------------------------------------
+ 19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
+ 19/11/2023 llundblade Add UsefulOutBuf_Swap().
+ 19/11/2023 llundblade Add UsefulOutBuf_Compare().
19/12/2022 llundblade Document that adding empty data is allowed.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf.
9/21/2021 llundbla Clarify UsefulOutBuf size calculation mode
@@ -853,7 +856,7 @@
/**
- * @brief Initialize and supply the actual output buffer.
+ * @brief Initialize and supply the output buffer.
*
* @param[out] pUOutBuf The @ref UsefulOutBuf to initialize.
* @param[in] Storage Buffer to output into.
@@ -1335,7 +1338,7 @@
/**
- * @brief Returns the resulting valid data in a UsefulOutBuf
+ * @brief Returns the data put into a UsefulOutBuf.
*
* @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
*
@@ -1353,7 +1356,7 @@
/**
- * @brief Copies the valid data into a supplied buffer
+ * @brief Copy out the data put into a UsefulOutBuf.
*
* @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
* @param[out] Dest The destination buffer to copy into.
@@ -1363,11 +1366,98 @@
* state was entered.
*
* This is the same as UsefulOutBuf_OutUBuf() except it copies the
- * data to @c Dest.
+ * data to @c Dest rather than returning a pointer.
*/
UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *pUOutBuf, UsefulBuf Dest);
+/**
+ * @brief Returns data starting at an offset that was put into a UsefulOutBuf.
+ *
+ * @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
+ * @param[in] uOffset Offset to bytes to return.
+ *
+ * @return NULLUsefulBufC or the bytes at the offset.
+ *
+ * This is the same as UsefulOutBuf_OutUBuf() except a starting offset
+ * maybe specified. It returns the bytes starting at @c uOffset to the
+ * end of what was encoded so far. Calling this with @c uOffset 0 is
+ * equivalent to UsefulOutBuf_OutUBuf().
+ *
+ * If there's nothing at @c uOffset or it is past the in the output
+ * buffer, a \ref NULLUsefulBufC is returned.
+ *
+ * This is typically not needed in typical use. It is used by QCBOR
+ * along with UsefulOutBuf_Compare() and UsefulOutBuf_Swap() for
+ * sorting CBOR maps.
+ */
+UsefulBufC
+UsefulOutBuf_OutUBufOffset(UsefulOutBuf *pUOutBuf, size_t uOffset);
+
+
+/**
+ * @brief Compare bytes at offsets.
+ *
+ * @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
+ * @param[in] uStart1 Offset of first bytes to compare.
+ * @param[in] uStart2 Offset of second bytes to compare.
+ *
+ * @return 0 for equality, positive if uStart1 is lexographically larger,
+ * negative if uStart2 is lexographically larger.
+ *
+ * This looks into bytes that have been output at the offsets @c start1
+ * and @c start2. It compares bytes at those two starting points until
+ * they are not equal or the end of the output data is reached from
+ * one of the starting points.
+ *
+ * This returns positive when @c uStart1 lexographically sorts ahead
+ * of @c uStart2 and vice versa. Zero is returned if the strings
+ * compare equally. This only happens when the end of the valid data
+ * is reached from one of the starting points and the comparison up to
+ * that point is equality.
+ *
+ * If either start is past the end of data in the output buffer, 0
+ * will be returned. It is the caller's responsibility to make sure
+ * the offsets are not off the end such that a comparison is actually
+ * being made. No data will ever be read off the end of the buffer so
+ * this safe no matter what offsets are passed.
+ *
+ * This is a relatively odd function in that it works on data in the
+ * output buffer. It is employed by QCBOR to sort CBOR-encoded maps that
+ * are in the output buffer.
+ */
+int UsefulOutBuf_Compare(UsefulOutBuf *pUOutBuf, size_t uStart1, size_t uStart2);
+
+
+/**
+ * @brief Swap two regions of output bytes.
+ *
+ * @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
+ * @param[in] uStartOffset Offset to start of bytes to be swapped.
+ * @param[in] uPivotOffset Offset to pivot around which bytes are swapped.
+ * @param[in] uEndOffset Offset to end of region to be swappe.
+ *
+ * This reaches into bytes that have been output and swaps two
+ * adjacent regions.
+ *
+ * If any of the offsets are outside the range of valid data, no
+ * swapping will be performed. If the start is not the smallest and
+ * the pivot is not in the middle no swapping will be performed.
+ *
+ * The byte at @c uStartOffset will participate in the swapping. The
+ * byte at @c uEndOffset will not participate in the swapping, only
+ * the byte before it.
+ *
+ * This is a relatively odd function in that it works on data in the
+ * output buffer. It is employed by QCBOR to bubble sort encoded CBOR
+ * maps.
+ */
+void UsefulOutBuf_Swap(UsefulOutBuf *pUOutBuf,
+ size_t uStartOffset,
+ size_t uPivotOffset,
+ size_t uEndOffset);
+
+
/**
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 8b2ed90..3df9027 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -1758,6 +1758,29 @@
/**
+ * @brief Close and sort an open map.
+ *
+ * @param[in] pCtx The encoding context to close the map in .
+ *
+ * This is the same as QCBOREncode_CloseMap() except it sorts the map
+ * per RFC 8949 Section 4.2.1. This sort is lexicographic of the CBOR-
+ * encoded map labels.
+ *
+ * This is more expensive than most things in the encoder. It uses
+ * bubble sort which runs in n-squared time where n is the number of
+ * map items. Sorting large maps on slow CPUs might be slow. This is
+ * also increases the object code size of the encoder by about 30%.
+ *
+ * Bubble sort was selected so as to not need an extra buffer to track
+ * map item offsets. Bubble sort works well even though map items are
+ * not all the same size because it always swaps adjacent items.
+ */
+void QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pCtx);
+
+void QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pCtx);
+
+
+/**
@brief Indicate start of encoded CBOR to be wrapped in a bstr.
@param[in] pCtx The encoding context to open the bstr-wrapped CBOR in.
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index b36e5d0..847395c 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2022, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -41,9 +41,12 @@
when who what, where, why
-------- ---- ---------------------------------------------------
+ 19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
+ 19/11/2023 llundblade Add UsefulOutBuf_Swap().
+ 19/11/2023 llundblade Add UsefulOutBuf_Compare().
19/12/2022 llundblade Don't pass NULL to memmove when adding empty data.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf
- 3/6/2021 mcr/llundblade Fix warnings related to --Wcast-qual
+ 3/6/2021 mcr/llundblade Fix warnings related to --Wcast-qual
01/28/2020 llundblade Refine integer signedness to quiet static analysis.
01/08/2020 llundblade Documentation corrections & improved code formatting.
11/08/2019 llundblade Re check pointer math and update comments
@@ -424,3 +427,105 @@
return result;
}
+
+/*
+ * Public function -- see UsefulBuf.h
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+int UsefulOutBuf_Compare(UsefulOutBuf *me, size_t uStart1, size_t uStart2)
+{
+ const uint8_t *pBase;
+ const uint8_t *pEnd;
+ const uint8_t *p1;
+ const uint8_t *p2;
+ int uComparison;
+
+ pBase = me->UB.ptr;
+ pEnd = (const uint8_t *)pBase + me->data_len;
+ p1 = pBase + uStart1;
+ p2 = pBase + uStart2;
+
+ uComparison = 0;
+ while(p1 < pEnd && p2 < pEnd) {
+ uComparison = *p2 - *p1;
+ if(uComparison != 0) {
+ break;;
+ }
+ p1++;
+ p2++;
+ }
+
+ return uComparison;
+}
+
+
+/**
+ * @brief Reverse order of bytes in a buffer.
+ *
+ * This reverses bytes starting at pStart, up to, but not including
+ * the byte at pEnd
+ */
+static void
+UsefulOutBuf_Private_ReverseBytes(uint8_t *pStart, uint8_t *pEnd)
+{
+ uint8_t uTmp;
+
+ while(pStart < pEnd) {
+ pEnd--;
+ uTmp = *pStart;
+ *pStart = *pEnd;
+ *pEnd = uTmp;
+ pStart++;
+ }
+}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+void UsefulOutBuf_Swap(UsefulOutBuf *pMe, size_t uStartOffset, size_t uPivotOffset, size_t uEndOffset)
+{
+ uint8_t *pBase;
+
+ if(uStartOffset > pMe->data_len || uPivotOffset > pMe->data_len || uEndOffset > pMe->data_len) {
+ return;
+ }
+
+ if(uStartOffset > uPivotOffset || uStartOffset > uEndOffset || uPivotOffset > uEndOffset) {
+ return;
+ }
+
+ /* This is the "reverse" algorithm to swap two memory regions */
+ pBase = pMe->UB.ptr;
+ UsefulOutBuf_Private_ReverseBytes(pBase + uStartOffset, pBase + uPivotOffset);
+ UsefulOutBuf_Private_ReverseBytes(pBase + uPivotOffset, pBase + uEndOffset);
+ UsefulOutBuf_Private_ReverseBytes(pBase + uStartOffset, pBase + uEndOffset);
+}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ */
+UsefulBufC
+UsefulOutBuf_OutUBufOffset(UsefulOutBuf *pMe, size_t uOffset)
+{
+ UsefulBufC ReturnValue;
+
+ ReturnValue = UsefulOutBuf_OutUBuf(pMe);
+
+ if(UsefulBuf_IsNULLC(ReturnValue)) {
+ return NULLUsefulBufC;
+ }
+
+ if(uOffset >= ReturnValue.len) {
+ return NULLUsefulBufC;
+ }
+
+ ReturnValue.ptr = (const uint8_t *)ReturnValue.ptr + uOffset;
+ ReturnValue.len -= uOffset;
+
+ return ReturnValue;
+}
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 53df657..754f7c5 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2022, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -570,7 +570,7 @@
(void)uMajorType;
(void)pMe;
#endif
-
+
return false;
}
@@ -778,6 +778,32 @@
/*
+ * Public functions for adding a double. See qcbor/qcbor_encode.h
+ */
+void QCBOREncode_AddDoubleDeterministic(QCBOREncodeContext *me, double dNum)
+{
+ if(dNum <= (double)UINT64_MAX && dNum >= 0) {
+ uint64_t uNum = (uint64_t)dNum;
+ if((double)uNum == dNum) {
+ QCBOREncode_AddUInt64(me, uNum);
+ return;
+ }
+ /* Fall through */
+ } else if(dNum >= (double)INT64_MIN && dNum < 0) {
+ int64_t nNum = (int64_t)dNum;
+ if((double)nNum == dNum) {
+ QCBOREncode_AddInt64(me, nNum);
+ return;
+ }
+ /* Fall through */
+ }
+ //const IEEE754_union uNum = IEEE754_DoubleToSmallest(dNum);
+
+ //QCBOREncode_AddType7(me, uNum.uSize, uNum.uValue);
+}
+
+
+/*
* Public functions for adding a float. See qcbor/qcbor_encode.h
*/
void QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *me, float fNum)
@@ -917,6 +943,299 @@
}
+
+/**
+ * @brief Decode a CBOR item head.
+ *
+ * @param[in] pUInBuf UsefulInputBuf to read from.
+ * @param[out] pnMajorType Major type of decoded head.
+ * @param[out] puArgument Argument of decoded head.
+ * @param[out] pnAdditionalInfo Additional info from decoded head.
+ *
+ * @return SUCCESS if a head was decoded
+ * HIT_END if there were not enough bytes to decode a head
+ * UNSUPPORTED if the decoded item is not one that is supported
+ *
+ * This is copied from qcbor_decode.c rather than referenced. This
+ * makes the core decoder 60 bytes smaller because it gets inlined.
+ * It would not get inlined if it was referenced. It is important to
+ * make the core decoder as small as possible. The copy here does make
+ * map sorting 200 bytes bigger, but map sorting is rarely used in
+ * environments that need small object code. It would also make
+ * qcbor_encode.c depend on qcbor_decode.c
+ *
+ * This is also super stable and tested. It implements the very
+ * well-defined part of CBOR that will never change. So this won't
+ * change.
+ */
+static QCBORError
+QCBOREncodePriv_DecodeHead(UsefulInputBuf *pUInBuf,
+ int *pnMajorType,
+ uint64_t *puArgument,
+ int *pnAdditionalInfo)
+{
+ QCBORError uReturn;
+
+ /* Get the initial byte that every CBOR data item has and break it
+ * down. */
+ const int nInitialByte = (int)UsefulInputBuf_GetByte(pUInBuf);
+ const int nTmpMajorType = nInitialByte >> 5;
+ const int nAdditionalInfo = nInitialByte & 0x1f;
+
+ /* Where the argument accumulates */
+ uint64_t uArgument;
+
+ 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);
+ }
+ } else if(nAdditionalInfo >= ADDINFO_RESERVED1 && nAdditionalInfo <= ADDINFO_RESERVED3) {
+ /* The reserved and thus-far unused additional info values */
+ uReturn = QCBOR_ERR_UNSUPPORTED;
+ goto Done;
+ } else {
+ /* 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 Consume the next item from a UsefulInputBuf.
+ *
+ * @param[in] pInBuf UsefulInputBuf from which to consume item.
+ *
+ * Recursive, but stack usage is light and encoding depth limit
+ */
+static QCBORError
+QCBOREncodePriv_ConsumeNext(UsefulInputBuf *pInBuf)
+{
+ int nMajor;
+ uint64_t uArgument;
+ int nAdditional;
+ uint16_t uItemCount;
+ uint16_t uMul;
+ uint16_t i;
+ QCBORError uCBORError;
+
+ uCBORError = QCBOREncodePriv_DecodeHead(pInBuf, &nMajor, &uArgument, &nAdditional);
+ if(uCBORError != QCBOR_SUCCESS) {
+ return uCBORError;
+ }
+
+ uMul = 1;
+
+ switch(nMajor) {
+ case CBOR_MAJOR_TYPE_POSITIVE_INT: /* Major type 0 */
+ case CBOR_MAJOR_TYPE_NEGATIVE_INT: /* Major type 1 */
+ break;
+
+ case CBOR_MAJOR_TYPE_SIMPLE:
+ return uArgument == CBOR_SIMPLE_BREAK ? 1 : 0;
+ break;
+
+ case CBOR_MAJOR_TYPE_BYTE_STRING:
+ case CBOR_MAJOR_TYPE_TEXT_STRING:
+ if(nAdditional == LEN_IS_INDEFINITE) {
+ /* Segments of indefinite length */
+ while(QCBOREncodePriv_ConsumeNext(pInBuf) == 0);
+ }
+ (void)UsefulInputBuf_GetBytes(pInBuf, uArgument);
+ break;
+
+ case CBOR_MAJOR_TYPE_TAG:
+ QCBOREncodePriv_ConsumeNext(pInBuf);
+ break;
+
+ case CBOR_MAJOR_TYPE_MAP:
+ uMul = 2;
+ /* Fallthrough */
+ case CBOR_MAJOR_TYPE_ARRAY:
+ uItemCount = (uint16_t)uArgument * uMul;
+ if(nAdditional == LEN_IS_INDEFINITE) {
+ uItemCount = UINT16_MAX;
+ }
+ for(i = uItemCount; i > 0; i--) {
+ if(QCBOREncodePriv_ConsumeNext(pInBuf)) {
+ /* End of indefinite length */
+ break;
+ }
+ }
+ break;
+ }
+
+ return QCBOR_SUCCESS;
+}
+
+
+/**
+ * @brief Decoded next item to get its length.
+ *
+ * Decode the next item in map no matter what type it is. It works
+ * recursively when an item is a map or array It returns offset just
+ * past the item decoded or zero there are no more items in the output
+ * buffer.
+ *
+ * This doesn't distinguish between end of the input and an error
+ * because it is used to decode stuff we encoded into a buffer, not
+ * stuff that came in from outside. We still want a check for safety
+ * in case of bugs here, but it is OK to report end of input on error.
+ */
+static uint32_t
+QCBOREncodePriv_DecodeNextInMap(QCBOREncodeContext *pMe, uint32_t uStart)
+{
+ UsefulInputBuf InBuf;
+ UsefulBufC EncodedMapBytes;
+ QCBORError uCBORError;
+
+ EncodedMapBytes = UsefulOutBuf_OutUBufOffset(&(pMe->OutBuf), uStart);
+ if(UsefulBuf_IsNULLC(EncodedMapBytes)) {
+ return 0;
+ }
+
+ UsefulInputBuf_Init(&InBuf, EncodedMapBytes);
+
+ /* This is always used on maps, so consume two, the label and the value */
+ uCBORError = QCBOREncodePriv_ConsumeNext(&InBuf);
+ if(uCBORError) {
+ return 0;
+ }
+ uCBORError = QCBOREncodePriv_ConsumeNext(&InBuf);
+ if(uCBORError) {
+ return 0;
+ }
+
+ /* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
+ return (uint32_t)UsefulInputBuf_Tell(&InBuf);
+}
+
+
+/**
+ * @brief Sort items lexographically by encoded labels.
+ *
+ * @param[in] pMe Encoding context.
+ * @param[in] uStart Offset in outbuf of first item for sorting.
+ *
+ * This reaches into the UsefulOutBuf in the encoding context and
+ * sorts encoded CBOR items. The byte offset start of the items is at
+ * @c uStart and it goes to the end of valid bytes in the
+ * UsefulOutBuf.
+ */
+static void
+QCBOREncodePriv_SortMap(QCBOREncodeContext *pMe, uint32_t uStart)
+{
+ bool bSwapped;
+ int nComparison;
+ uint32_t uLen2;
+ uint32_t uLen1;
+ uint32_t uStart1;
+ uint32_t uStart2;
+
+ if(pMe->uError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ /* Bubble sort because the sizes of all the items are not the
+ * same. It works with adjacent pairs so the swap is not too
+ * difficult even though sizes are different.
+ *
+ * While bubble sort is n-squared, it seems OK here because n will
+ * usually be small and the comparison and swap functions aren't
+ * too CPU intensive.
+ *
+ * Another approach would be to have an array of offsets to the
+ * items. However this requires memory allocation and the swap
+ * operation for quick sort or such is complicated because the item
+ * sizes are not the same and overlap may occur in the bytes being
+ * swapped.
+ */
+ do {
+ uLen1 = QCBOREncodePriv_DecodeNextInMap(pMe, uStart);
+ if(uLen1 == 0) {
+ /* It's an empty map. Nothing to do. */
+ break;
+ }
+ uStart1 = uStart;
+ uStart2 = uStart1 + uLen1;
+ bSwapped = false;
+
+ while(1) {
+ uLen2 = QCBOREncodePriv_DecodeNextInMap(pMe, uStart2);
+ if(uLen2 == 0) {
+ break;
+ }
+
+ nComparison = UsefulOutBuf_Compare(&(pMe->OutBuf), uStart1, uStart2);
+ if(nComparison < 0) {
+ UsefulOutBuf_Swap(&(pMe->OutBuf), uStart1, uStart2, uStart2 + uLen2);
+ uStart1 = uStart1 + uLen2;
+ bSwapped = true;
+ } else {
+ uStart1 = uStart2;
+ }
+ uStart2 = uStart2 + uLen2;
+ }
+ } while(bSwapped);
+}
+
+
+/*
+ * Public functions for closing sorted maps. See qcbor/qcbor_encode.h
+ */
+void QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pMe)
+{
+ uint32_t uStart;
+
+ /* The Header for the map we are about to sort hasn't been
+ * inserted yet, so uStart is the position of the first item
+ * and the end out the UsefulOutBuf data is the end of the
+ * items we are about to sort.
+ */
+ uStart = Nesting_GetStartPos(&(pMe->nesting));
+ QCBOREncodePriv_SortMap(pMe, uStart);
+
+ QCBOREncode_CloseMapOrArray(pMe, CBOR_MAJOR_TYPE_MAP);
+}
+
+
+/*
+ * Public functions for closing sorted maps. See qcbor/qcbor_encode.h
+ */
+void QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pMe)
+{
+ uint32_t uStart;
+
+ uStart = Nesting_GetStartPos(&(pMe->nesting));
+ QCBOREncodePriv_SortMap(pMe, uStart);
+
+ QCBOREncode_CloseMapOrArrayIndefiniteLength(pMe, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
+}
+
+
/*
* Public functions for closing bstr wrapping. See qcbor/qcbor_encode.h
*/
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index e93a011..e6be249 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2022, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -873,3 +873,185 @@
return NULL;
}
+
+
+const char * UOBExtraTests(void)
+{
+ #define COMPARE_TEST_SIZE 10
+ UsefulOutBuf_MakeOnStack( UOB, COMPARE_TEST_SIZE);
+ int nCompare;
+ UsefulBufC Out;
+
+ /* Test UsefulOutBuf_Compare() */
+ UsefulOutBuf_AppendString(&UOB, "abcabdefab");
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 8);
+ if(nCompare != 0) {
+ return "ab should compare equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 3);
+ if(nCompare != 'd' - 'c') {
+ return "abc should not equal abd";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 3, 8);
+ if(nCompare != 0) {
+ return "ab should compare equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 2, 5);
+ if(nCompare != 'd' - 'c') {
+ return "ca should not equal de";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 5, 2);
+ if(nCompare != 'c' - 'd') {
+ return "de should not equal ca";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 7, 8);
+ if(nCompare != 'a' - 'f') {
+ return "fa should not equal ab";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 0);
+ if(nCompare != 0) {
+ return "comparison to self failed";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 9, 9);
+ if(nCompare != 0) {
+ return "b should compare equal to b";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 10, 10);
+ if(nCompare != 0) {
+ return "Comparison off the end is equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 100);
+ if(nCompare != 0) {
+ return "Comparison off the end is equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 100, 0);
+ if(nCompare != 0) {
+ return "Comparison off the end is equal";
+ }
+
+ /* Test UsefulOutBuf_Swap() */
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 4, 8);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("efghabcd"))) {
+ return "swap fail 1";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 1, 2);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("bacdefgh"))) {
+ return "swap fail 2";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 1, 8);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("bcdefgha"))) {
+ return "swap fail 3";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 3, 4);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("dabcefgh"))) {
+ return "swap fail 4";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 9, 10, 11);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 5";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 4, 11);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 6";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 9, 0, 0);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 7";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 0, 0);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 8";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 8, 4, 0);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 9";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 8, 4);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 10";
+ }
+
+
+ /* Test for UsefulOutBuf_GetOutput() */
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abc");
+ UsefulOutBuf_AppendString(&UOB, "xyz");
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 0);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcxyz"))) {
+ return "GetOutput fail 1";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 5);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("z"))) {
+ return "GetOutput fail 2";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 1);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("bcxyz"))) {
+ return "GetOutput fail 3";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 6);
+ if(!UsefulBuf_IsNULLC(Out)) {
+ return "GetOutput fail 4";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 7);
+ if(!UsefulBuf_IsNULLC(Out)) {
+ return "GetOutput fail 5";
+ }
+
+ return NULL;
+}
diff --git a/test/UsefulBuf_Tests.h b/test/UsefulBuf_Tests.h
index 235358e..e00aa37 100644
--- a/test/UsefulBuf_Tests.h
+++ b/test/UsefulBuf_Tests.h
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -52,4 +52,6 @@
const char * UBAdvanceTest(void);
+const char * UOBExtraTests(void);
+
#endif
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 5c59fe1..170b068 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
Copyright (c) 2022, Arm Limited. All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -3080,3 +3080,299 @@
return 0;
}
+
+
+
+int32_t
+SortMapTest(void)
+{
+ UsefulBuf_MAKE_STACK_UB( TestBuf, 200);
+ QCBOREncodeContext EC;
+ UsefulBufC EncodedAndSorted;
+ QCBORError uErr;
+ struct UBCompareDiagnostic CompareDiagnostics;
+
+
+ /* --- Basic sort test case --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 3, 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 4, 4);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 11;
+ }
+
+ static const uint8_t spBasic[] = {
+ 0xA4, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04};
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBasic),
+ &CompareDiagnostics)) {
+ return 12;
+ }
+
+ /* --- Empty map sort test case --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 21;
+ }
+
+ static const uint8_t spEmpty[] = {0xA0};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEmpty),
+ &CompareDiagnostics)) {
+ return 22;
+ }
+
+ /* --- Several levels of nested sorted maps --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "three", 3);
+ QCBOREncode_OpenMapInMapN(&EC, 428);
+ QCBOREncode_AddNULLToMap(&EC, "null");
+ QCBOREncode_OpenArrayInMap(&EC, "array");
+ QCBOREncode_AddSZString(&EC, "hi");
+ QCBOREncode_AddSZString(&EC, "there");
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty2");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty1");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddDateEpochToMapN(&EC, 88, 888888);
+ QCBOREncode_AddBoolToMap(&EC, "boo", true);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 31;
+ }
+ static const uint8_t spNested[] = {
+ 0xA4, 0x18, 0x58, 0xC1, 0x1A, 0x00, 0x0D, 0x90,
+ 0x38, 0x19, 0x01, 0xAC, 0xA4, 0x64, 0x6E, 0x75,
+ 0x6C, 0x6C, 0xF6, 0x65, 0x61, 0x72, 0x72, 0x61,
+ 0x79, 0x82, 0x62, 0x68, 0x69, 0x65, 0x74, 0x68,
+ 0x65, 0x72, 0x65, 0x66, 0x65, 0x6D, 0x70, 0x74,
+ 0x79, 0x31, 0xA0, 0x66, 0x65, 0x6D, 0x70, 0x74,
+ 0x79, 0x32, 0xA0, 0x63, 0x62, 0x6F, 0x6F, 0xF5,
+ 0x65, 0x74, 0x68, 0x72, 0x65, 0x65, 0x03};
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spNested),
+ &CompareDiagnostics)) {
+ return 32;
+ }
+
+ /* --- Degenerate case of everything in order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 3);
+ QCBOREncode_AddInt64ToMap(&EC, "b", 4);
+ QCBOREncode_AddInt64ToMap(&EC, "aa", 5);
+ QCBOREncode_AddInt64ToMap(&EC, "aaa", 6);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 41;
+ }
+
+ static const uint8_t sp6Items[] = {
+ 0xA7, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x61,
+ 0x61, 0x03, 0x61, 0x62, 0x04, 0x62, 0x61, 0x61,
+ 0x05, 0x63, 0x61, 0x61, 0x61, 0x06};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sp6Items),
+ &CompareDiagnostics)) {
+ return 42;
+ }
+
+ /* --- Degenerate case -- reverse order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "aaa", 6);
+ QCBOREncode_AddInt64ToMap(&EC, "aa", 5);
+ QCBOREncode_AddInt64ToMap(&EC, "b", 4);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 51;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sp6Items),
+ &CompareDiagnostics)) {
+ return 52;
+ }
+
+ /* --- Same items, randomly out of order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "aa", 5);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMap(&EC, "b", 4);
+ QCBOREncode_AddInt64ToMap(&EC, "aaa", 6);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 61;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sp6Items),
+ &CompareDiagnostics)) {
+ return 62;
+ }
+
+ /* --- Stuff in front of and after array to sort --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddInt64(&EC, 111);
+ QCBOREncode_AddInt64(&EC, 222);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddInt64(&EC, 888);
+ QCBOREncode_AddInt64(&EC, 999);
+ QCBOREncode_CloseArray(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 71;
+ }
+
+ static const uint8_t spPreItems[] = {
+ 0x85, 0x18, 0x6F, 0x18, 0xDE, 0xA3, 0x00, 0x00,
+ 0x01, 0x01, 0x02, 0x02, 0x19, 0x03, 0x78, 0x19,
+ 0x03, 0xE7};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spPreItems),
+ &CompareDiagnostics)) {
+ return 72;
+ }
+
+ /* --- map with labels of all CBOR major types and in reverse order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+
+ QCBOREncode_AddDouble(&EC, 8.77);
+ QCBOREncode_AddInt64(&EC, 7);
+
+ QCBOREncode_AddBool(&EC, true);
+ QCBOREncode_AddInt64(&EC, 6);
+
+ QCBOREncode_AddDateEpoch(&EC, 88);
+ QCBOREncode_AddInt64(&EC, 5);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\xa0"));
+ QCBOREncode_AddInt64(&EC, 4);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\x80"));
+ QCBOREncode_AddInt64(&EC, 7);
+
+ QCBOREncode_AddInt64ToMap(&EC, "text", 3);
+
+ QCBOREncode_AddBytes(&EC, UsefulBuf_FromSZ("xx"));
+ QCBOREncode_AddInt64(&EC, 2);
+
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1); /* Integer */
+ QCBOREncode_CloseAndSortMap(&EC);
+
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 81;
+ }
+
+ static const uint8_t spLabelTypes[] = {
+ 0xA8, 0x01, 0x01, 0x42, 0x78, 0x78, 0x02, 0x64,
+ 0x74, 0x65, 0x78, 0x74, 0x03, 0x80, 0x07, 0xA0,
+ 0x04, 0xC1, 0x18, 0x58, 0x05, 0xF5, 0x06, 0xFB,
+ 0x40, 0x21, 0x8A, 0x3D, 0x70, 0xA3, 0xD7, 0x0A,
+ 0x07};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLabelTypes),
+ &CompareDiagnostics)) {
+ return 82;
+ }
+
+ /* --- labels are indefinitely encoded --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+
+ QCBOREncode_AddInt64ToMap(&EC, "aaaa", 1);
+
+ QCBOREncode_AddInt64ToMap(&EC, "bb", 2);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\x7f\x61" "a" "\x61" "a" "\xff"));
+ QCBOREncode_AddInt64(&EC, 3);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\x7f" "\x61" "c" "\xff"));
+ QCBOREncode_AddInt64(&EC, 4);
+
+ QCBOREncode_CloseAndSortMap(&EC);
+
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 91;
+ }
+
+ static const uint8_t spIndefItems[] = {
+ 0xA4, 0x62, 0x62, 0x62, 0x02, 0x64, 0x61, 0x61,
+ 0x61, 0x61, 0x01, 0x7F, 0x61, 0x61, 0x61, 0x61,
+ 0xFF, 0x03, 0x7F, 0x61, 0x63, 0xFF, 0x04};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefItems),
+ &CompareDiagnostics)) {
+ return 92;
+ }
+
+ /* --- Indefinitely encoded maps --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMapIndefiniteLength(&EC);
+
+ QCBOREncode_OpenMapIndefiniteLengthInMap(&EC, "aa");
+ QCBOREncode_CloseMapIndefiniteLength(&EC);
+
+ QCBOREncode_OpenArrayIndefiniteLengthInMap(&EC, "ff");
+ QCBOREncode_CloseArrayIndefiniteLength(&EC);
+
+ QCBOREncode_OpenMapIndefiniteLengthInMap(&EC, "zz");
+ QCBOREncode_CloseMapIndefiniteLength(&EC);
+
+ QCBOREncode_OpenMapIndefiniteLengthInMap(&EC, "bb");
+ QCBOREncode_CloseMapIndefiniteLength(&EC);
+
+ QCBOREncode_CloseAndSortMapIndef(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 101;
+ }
+
+ static const uint8_t spIndeMaps[] = {
+ 0xBF, 0x62, 0x61, 0x61, 0xBF, 0xFF, 0x62, 0x62,
+ 0x62, 0xBF, 0xFF, 0x62, 0x66, 0x66, 0x9F, 0xFF,
+ 0x62, 0x7A, 0x7A, 0xBF, 0xFF, 0xFF, 0x06, 0xFB};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndeMaps),
+ &CompareDiagnostics)) {
+ return 102;
+ }
+
+ return 0;
+}
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index bac1085..5271fd4 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2022, Laurence Lundblade.
+ Copyright (c) 2018-2023, Laurence Lundblade.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -192,5 +192,8 @@
int32_t OpenCloseBytesTest(void);
+/* Test map sorting */
+int32_t SortMapTest(void);
+
#endif /* defined(__QCBOR__qcbor_encode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index f2baaf1..8f91a9a 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -1,7 +1,7 @@
/*==============================================================================
run_tests.c -- test aggregator and results reporting
- Copyright (c) 2018-2021, Laurence Lundblade. All rights reserved.
+ Copyright (c) 2018-2023, Laurence Lundblade. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
@@ -61,7 +61,8 @@
TEST_ENTRY(UBMacroConversionsTest),
TEST_ENTRY(UBUtilTests),
TEST_ENTRY(UIBTest_IntegerFormat),
- TEST_ENTRY(UBAdvanceTest)
+ TEST_ENTRY(UBAdvanceTest),
+ TEST_ENTRY(UOBExtraTests)
};
@@ -148,7 +149,8 @@
TEST_ENTRY(ExponentAndMantissaEncodeTests),
#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
TEST_ENTRY(ParseEmptyMapInMapTest),
- TEST_ENTRY(BoolTest)
+ TEST_ENTRY(BoolTest),
+ TEST_ENTRY(SortMapTest)
};