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;
 }