Duplicate map label detection for encoding (#209)
This adds duplicate map label detection during encoding as part of sorting.
There was a lot of rework of map sorting.
UsefulOutBuf_Compare() was changed to behave differently and more universally.
* Duplicate detection for encoding
* rework UsefulOutBuf_Compare and test
* Dup detection seems to be working
* Final tidy-up
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index eb0a691..1a4a3bf 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-2024, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited. All rights reserved.
* All rights reserved.
*
@@ -43,6 +43,7 @@
when who what, where, why
-------- ---- --------------------------------------------------
+ 28/02/2022 llundblade Rearrange UsefulOutBuf_Compare().
19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
19/11/2023 llundblade Add UsefulOutBuf_Swap().
19/11/2023 llundblade Add UsefulOutBuf_Compare().
@@ -1401,34 +1402,41 @@
*
* @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.
+ * @param[in] uLen1 Length of first bytes to compare.
+ * @param[in] uStart2 Offset of second bytes to compare.
+ * @param[in] uLen2 Length 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.
+ * they are not equal or @c uLen1 or @c uLen2 is reached. If the
+ * length of the string given is off the end of the output data, the
+ * string will be effectively concated to the data in the output
+ * buffer for the comparison.
*
* 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.
+ * compare equally.
+ *
+ * If lengths are unequal and the first bytes are an exact subset of
+ * the second string, then a positve value will be returned and vice
+ * versa.
*
* 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
+ * the offsets are not off the end so 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.
+ * 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);
-
+int UsefulOutBuf_Compare(UsefulOutBuf *pUOutBuf,
+ size_t uStart1, size_t uLen1,
+ size_t uStart2, size_t uLen2);
/**
* @brief Swap two regions of output bytes.
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index f27af1f..694c2a3 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -2188,7 +2188,7 @@
* This is the same as QCBOREncode_CloseMap(), but the open map that
* is being close must be of indefinite length.
*/
-static void
+static void
QCBOREncode_CloseMapIndefiniteLength(QCBOREncodeContext *pCtx);
@@ -2198,22 +2198,25 @@
* @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.
+ * per RFC 8949 Section 4.2.1 and checks for duplicate map keys. 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 which runs in n-squared time where @c 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%
* (500-1000 bytes).
*
- * 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.
+ * Bubble sort was selected so as to not need require configuration of
+ * a 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_CloseAndSortMap(QCBOREncodeContext *pCtx);
-void QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pCtx);
+void
+QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pCtx);
/**
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index 847395c..dae4eb1 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2023, Laurence Lundblade.
+ Copyright (c) 2018-2024, Laurence Lundblade.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -41,6 +41,7 @@
when who what, where, why
-------- ---- ---------------------------------------------------
+ 28/02/2022 llundblade Rearrange UsefulOutBuf_Compare().
19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
19/11/2023 llundblade Add UsefulOutBuf_Swap().
19/11/2023 llundblade Add UsefulOutBuf_Compare().
@@ -433,21 +434,27 @@
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
-int UsefulOutBuf_Compare(UsefulOutBuf *me, size_t uStart1, size_t uStart2)
+int UsefulOutBuf_Compare(UsefulOutBuf *pMe,
+ const size_t uStart1, const size_t uLen1,
+ const size_t uStart2, const size_t uLen2)
{
const uint8_t *pBase;
const uint8_t *pEnd;
const uint8_t *p1;
const uint8_t *p2;
+ const uint8_t *p1End;
+ const uint8_t *p2End;
int uComparison;
- pBase = me->UB.ptr;
- pEnd = (const uint8_t *)pBase + me->data_len;
+ pBase = pMe->UB.ptr;
+ pEnd = (const uint8_t *)pBase + pMe->data_len;
p1 = pBase + uStart1;
p2 = pBase + uStart2;
+ p1End = p1 + uLen1;
+ p2End = p2 + uLen2;
uComparison = 0;
- while(p1 < pEnd && p2 < pEnd) {
+ while(p1 < pEnd && p2 < pEnd && p1 < p1End && p2 < p2End) {
uComparison = *p2 - *p1;
if(uComparison != 0) {
break;;
@@ -456,10 +463,21 @@
p2++;
}
+ if(uComparison == 0 && p1 != p1End && p2 != p2End) {
+ if(uLen1 > uLen2) {
+ uComparison = 1;
+ } else if(uLen2 < uLen1){
+ uComparison = -1;
+ } else {
+ return 0;
+ }
+ }
+
return uComparison;
}
+
/**
* @brief Reverse order of bytes in a buffer.
*
@@ -473,7 +491,7 @@
while(pStart < pEnd) {
pEnd--;
- uTmp = *pStart;
+ uTmp = *pStart;
*pStart = *pEnd;
*pEnd = uTmp;
pStart++;
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 37b1c89..e4a7bf6 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -1287,7 +1287,7 @@
/**
- * @brief Decoded next item to get its length.
+ * @brief Decoded next item to get its lengths.
*
* 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
@@ -1299,16 +1299,25 @@
* 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
+struct ItemLens {
+ uint32_t uLabelLen;
+ uint32_t uItemLen;
+};
+
+static struct ItemLens
QCBOREncode_Private_DecodeNextInMap(QCBOREncodeContext *pMe, uint32_t uStart)
{
- UsefulInputBuf InBuf;
- UsefulBufC EncodedMapBytes;
- QCBORError uCBORError;
+ UsefulInputBuf InBuf;
+ UsefulBufC EncodedMapBytes;
+ QCBORError uCBORError;
+ struct ItemLens Result;
+
+ Result.uLabelLen = 0;
+ Result.uItemLen = 0;
EncodedMapBytes = UsefulOutBuf_OutUBufOffset(&(pMe->OutBuf), uStart);
if(UsefulBuf_IsNULLC(EncodedMapBytes)) {
- return 0;
+ return Result;
}
UsefulInputBuf_Init(&InBuf, EncodedMapBytes);
@@ -1316,15 +1325,22 @@
/* This is always used on maps, so consume two, the label and the value */
uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
if(uCBORError) {
- return 0;
- }
- uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
- if(uCBORError) {
- return 0;
+ return Result;
}
/* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
- return (uint32_t)UsefulInputBuf_Tell(&InBuf);
+ Result.uLabelLen = (uint32_t)UsefulInputBuf_Tell(&InBuf);
+
+ uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
+ if(uCBORError) {
+ Result.uLabelLen = 0;
+ return Result;
+ }
+
+ Result.uItemLen = (uint32_t)UsefulInputBuf_Tell(&InBuf);
+
+ /* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
+ return Result;
}
@@ -1342,12 +1358,13 @@
static void
QCBOREncode_Private_SortMap(QCBOREncodeContext *pMe, uint32_t uStart)
{
- bool bSwapped;
- int nComparison;
- uint32_t uLen2;
- uint32_t uLen1;
- uint32_t uStart1;
- uint32_t uStart2;
+ bool bSwapped;
+ int nComparison;
+ uint32_t uStart1;
+ uint32_t uStart2;
+ struct ItemLens Lens1;
+ struct ItemLens Lens2;
+
if(pMe->uError != QCBOR_SUCCESS) {
return;
@@ -1367,31 +1384,38 @@
* sizes are not the same and overlap may occur in the bytes being
* swapped.
*/
- do {
- uLen1 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart);
- if(uLen1 == 0) {
+ do { /* Loop until nothing was swapped */
+ Lens1 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart);
+ if(Lens1.uLabelLen == 0) {
/* It's an empty map. Nothing to do. */
break;
}
uStart1 = uStart;
- uStart2 = uStart1 + uLen1;
+ uStart2 = uStart1 + Lens1.uItemLen;
bSwapped = false;
while(1) {
- uLen2 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart2);
- if(uLen2 == 0) {
+ Lens2 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart2);
+ if(Lens2.uLabelLen == 0) {
break;
}
- nComparison = UsefulOutBuf_Compare(&(pMe->OutBuf), uStart1, uStart2);
+ nComparison = UsefulOutBuf_Compare(&(pMe->OutBuf),
+ uStart1, Lens1.uLabelLen,
+ uStart2, Lens2.uLabelLen);
if(nComparison < 0) {
- UsefulOutBuf_Swap(&(pMe->OutBuf), uStart1, uStart2, uStart2 + uLen2);
- uStart1 = uStart1 + uLen2;
+ UsefulOutBuf_Swap(&(pMe->OutBuf), uStart1, uStart2, uStart2 + Lens2.uItemLen);
+ uStart1 = uStart1 + Lens2.uItemLen; /* item 2 now in position of item 1 */
+ /* Lens1 is still valid as Lens1 for the next loop */
bSwapped = true;
- } else {
+ } else if(nComparison > 0) {
uStart1 = uStart2;
+ Lens1 = Lens2;
+ } else /* nComparison == 0 */ {
+ pMe->uError = QCBOR_ERR_DUPLICATE_LABEL;
+ return;
}
- uStart2 = uStart2 + uLen2;
+ uStart2 = uStart2 + Lens2.uItemLen;
}
} while(bSwapped);
}
@@ -1400,7 +1424,8 @@
/*
* Public functions for closing sorted maps. See qcbor/qcbor_encode.h
*/
-void QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pMe)
+void
+QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pMe)
{
uint32_t uStart;
@@ -1419,7 +1444,8 @@
/*
* Public functions for closing sorted maps. See qcbor/qcbor_encode.h
*/
-void QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pMe)
+void
+QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pMe)
{
uint32_t uStart;
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index e6be249..13e87e3 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-2023, Laurence Lundblade.
+ Copyright (c) 2018-2024, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -885,61 +885,81 @@
/* Test UsefulOutBuf_Compare() */
UsefulOutBuf_AppendString(&UOB, "abcabdefab");
- nCompare = UsefulOutBuf_Compare(&UOB, 0, 8);
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 2, 8, 2);
if(nCompare != 0) {
return "ab should compare equal";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 0, 3);
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 3, 3, 3);
if(nCompare != 'd' - 'c') {
return "abc should not equal abd";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 3, 8);
+ nCompare = UsefulOutBuf_Compare(&UOB, 3, 2, 8, 2);
if(nCompare != 0) {
return "ab should compare equal";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 2, 5);
+ nCompare = UsefulOutBuf_Compare(&UOB, 2, 4, 5, 4);
if(nCompare != 'd' - 'c') {
return "ca should not equal de";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 5, 2);
+ nCompare = UsefulOutBuf_Compare(&UOB, 5, 1, 2, 1);
if(nCompare != 'c' - 'd') {
return "de should not equal ca";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 7, 8);
+ nCompare = UsefulOutBuf_Compare(&UOB, 7, 2, 8, 2);
if(nCompare != 'a' - 'f') {
return "fa should not equal ab";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 0, 0);
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 10, 0, 10);
if(nCompare != 0) {
return "comparison to self failed";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 9, 9);
+ nCompare = UsefulOutBuf_Compare(&UOB, 9, 1, 9, 1);
if(nCompare != 0) {
return "b should compare equal to b";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 10, 10);
+ nCompare = UsefulOutBuf_Compare(&UOB, 10, 1, 10, 1);
if(nCompare != 0) {
return "Comparison off the end is equal";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 0, 100);
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 1, 100, 1);
if(nCompare != 0) {
return "Comparison off the end is equal";
}
- nCompare = UsefulOutBuf_Compare(&UOB, 100, 0);
+ nCompare = UsefulOutBuf_Compare(&UOB, 100, 1, 0, 1);
if(nCompare != 0) {
return "Comparison off the end is equal";
}
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 3, 3, 2);
+ if(nCompare > 0) {
+ return "Comparison of unequal lengths incorrect";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 8, 2, 0, 3);
+ if(nCompare < 0) {
+ return "Comparison of unequal lengths incorrect 2";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 2, 2, 3);
+ if(nCompare != 'c' - 'a') {
+ return "Inequal lengths, but inequal strings";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 1, 3, 4, 2);
+ if(nCompare != 'd' - 'c') {
+ return "Inequal lengths, but inequal strings";
+ }
+
/* Test UsefulOutBuf_Swap() */
UsefulOutBuf_Reset(&UOB);
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 7594006..118b232 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -3196,7 +3196,24 @@
if(uErr) {
return 31;
}
- static const uint8_t spNested[] = {
+
+ /* Correctly sorted.
+ * {
+ * 88: 1(888888),
+ * 428: {
+ * "null": null,
+ * "array": [
+ * "hi",
+ * "there"
+ * ],
+ * "empty1": {},
+ * "empty2": {}
+ * },
+ * "boo": true,
+ * "three": 3
+ * }
+ */
+ static const uint8_t spSorted[] = {
0xA4, 0x18, 0x58, 0xC1, 0x1A, 0x00, 0x0D, 0x90,
0x38, 0x19, 0x01, 0xAC, 0xA4, 0x64, 0x6E, 0x75,
0x6C, 0x6C, 0xF6, 0x65, 0x61, 0x72, 0x72, 0x61,
@@ -3207,11 +3224,72 @@
0x65, 0x74, 0x68, 0x72, 0x65, 0x65, 0x03};
if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
- UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spNested),
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSorted),
&CompareDiagnostics)) {
return 32;
}
+
+ /* Same data items, but added in a different order */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "three", 3);
+ QCBOREncode_OpenMapInMapN(&EC, 428);
+ QCBOREncode_OpenMapInMap(&EC, "empty1");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_OpenArrayInMap(&EC, "array");
+ QCBOREncode_AddSZString(&EC, "hi");
+ QCBOREncode_AddSZString(&EC, "there");
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty2");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddNULLToMap(&EC, "null");
+ 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;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSorted),
+ &CompareDiagnostics)) {
+ return 32;
+ }
+
+ /* Same data items, but added in a different order */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddBoolToMap(&EC, "boo", true);
+ QCBOREncode_OpenMapInMapN(&EC, 428);
+ QCBOREncode_OpenMapInMap(&EC, "empty1");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_OpenArrayInMap(&EC, "array");
+ QCBOREncode_AddSZString(&EC, "hi");
+ QCBOREncode_AddSZString(&EC, "there");
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty2");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddNULLToMap(&EC, "null");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddDateEpochToMapN(&EC, 88, 888888);
+ QCBOREncode_AddInt64ToMap(&EC, "three", 3);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 31;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSorted),
+ &CompareDiagnostics)) {
+ return 32;
+ }
+
+
+
/* --- Degenerate case of everything in order --- */
QCBOREncode_Init(&EC, TestBuf);
QCBOREncode_OpenMap(&EC);
@@ -3429,6 +3507,44 @@
return 102;
}
+
+ /* --- Duplicate label test --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 3, 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 114;
+ }
+
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 3, 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 2);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 115;
+ }
+
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "abc", 3);
+ QCBOREncode_AddInt64ToMap(&EC, "def", 1);
+ QCBOREncode_AddInt64ToMap(&EC, "def", 1);
+ QCBOREncode_AddInt64ToMap(&EC, "def", 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 116;
+ }
+
return 0;
}