Decode conformance for preferred serialization, CDE and dCBOR (#216)

Three conformance modes are added for decoding
- preferred serialization
- CDE
- dCBOR

This checks for sort ordering and duplicate labels when a map is decoded in CDE and dCBOR modes.

It does not support arrays and maps as map labels. They will error out, unless you use maps-as-arrays mode.

Conformance includes checking for shortest form of integers and floats and for dCBOR unification of floats and integers.


* start work on dCBOR decoding enforcement

* checkpoint

* Floating point conformane; ifdef for disabling

* Add more tests

* More dCBOR tests and conformance checks

* More test cases

* Bug fixes and more tests

* Check point stuff

* Map dup and sort order checking kind of working

* more work...

* Finish off UsefulInputBuf_Compare()

* Fix #ifdef fanout

* Fix warnings and #ifdef fan out

* sort & dup checking working and tested

* Fix test ifdef fan out

* Minor fix of one test case

* Another fan out fix

* backout map label checking; doc; test

* Stragglers

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 218d269..d67dd85 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -43,6 +43,7 @@
 
  when         who             what, where, why
  --------     ----            --------------------------------------------------
+ 1/7/2024     llundblade      Add UsefulInputBuf_Compare().
  10/05/2024   llundblade      Add Xxx_OffsetToPointer.
  28/02/2024   llundblade      Rearrange UsefulOutBuf_Compare().
  19/11/2023   llundblade      Add UsefulOutBuf_GetOutput().
@@ -1425,7 +1426,7 @@
  * and @c start2. It compares bytes at those two starting points until
  * 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
+ * string will be effectively truncated to the data in the output
  * buffer for the comparison.
  *
  * This returns positive when @c uStart1 lexographically sorts ahead
@@ -1822,6 +1823,32 @@
 static void UsefulInputBuf_SetBufferLength(UsefulInputBuf *pUInBuf, size_t uNewLen);
 
 
+/**
+ * @brief Compare two ranges of bytes somewhere in the input buffer.
+ *
+ * @param[in] pUInBuf   The input buffer.
+ * @param[in] uOffset1  Offset of first range of bytes.
+ * @param[in] uLen1     Length of first range of bytes.
+ * @param[in] uOffset2  Offset of second range of bytes.
+ * @param[in] uLen2     Length of second range of bytes.
+ *
+ * This returns the same as UsefulBuf_Compare().
+ * 
+ * If the offset or the length plus offset or a range extends outside
+ * the input buffer, that range of bytes will be considered greater
+ * than the other string. If both are outside this is considered a
+ * degenerate condition and the first string is considered larger.
+ *
+ * This is a somewhat odd function of UsefulInputBuf as it is not used
+ * for consuming data. QCBOR uses it for map order and duplicate
+ * checking.
+ */
+int
+UsefulInputBuf_Compare(UsefulInputBuf *pUInBuf,
+                       const size_t    uOffset1,
+                       const size_t    uLen1,
+                       const size_t    uOffset2,
+                       const size_t    uLen2);
 
 
 /*----------------------------------------------------------
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index d64e973..1441ade 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -43,8 +43,6 @@
 #endif
 #endif
 
-
-
 /**
  * @file qcbor_common.h
  *
@@ -531,15 +529,33 @@
     */
    QCBOR_ERR_NOT_PREFERRED = 79,
 
-   /* Trying to encode something that is discouraged (e.g., 65-bit
+   /** Trying to encode something that is discouraged (e.g., 65-bit
     * negative integer) without allowing it by calling
     * QCBOREncode_Allow() */
    QCBOR_ERR_NOT_ALLOWED = 80,
+
    /** QCBORDecode_EnterBstrWrapped() cannot be used on
-    * indefinite-length strings because they exist in memory pool for
+    * indefinite-length strings because they exist in the memory pool for
     * a @ref QCBORStringAllocate. */
    QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING = 81,
 
+   /** Decoded CBOR is does not conform to preferred serialization. The CBOR head's argument is not encoded in shortest form, or indefinite lengths are used.*/
+   QCBOR_ERR_PREFERRED_CONFORMANCE = 82,
+
+   /** Decoded CBOR does not conform to CDE. This occurs when a map is not sorted. Other
+    * CDE issues are reported as QCBOR_ERR_PREFERRED_CONFORMANCE. */
+   QCBOR_ERR_CDE_CONFORMANCE = 83,
+
+   /** Decoded CBOR does not conform to dCBOR. Floating point numbers are not reduced to integers.
+    * Other issues are reported as either QCBOR_ERR_CDE_CONFORMANCE or QCBOR_ERR_PREFERRED_CONFORMANCE. */
+   QCBOR_ERR_DCBOR_CONFORMANCE = 84,
+
+   /** A map is unsorted and should be for CDE or dCBOR. */
+   QCBOR_ERR_UNSORTED = 85,
+
+   /** Conformance checking requested, preferred serialization disabled, float in the input. */
+   QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE = 86,
+
    /** A range of error codes that can be made use of by the
     * caller. QCBOR internally does nothing with these except notice
     * that they are not QCBOR_SUCCESS. See QCBORDecode_SetError(). */
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index dc79912..c2da5a2 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -193,7 +193,34 @@
    /** See QCBORDecode_Init() */
    QCBOR_DECODE_MODE_MAP_STRINGS_ONLY = 1,
    /** See QCBORDecode_Init() */
-   QCBOR_DECODE_MODE_MAP_AS_ARRAY = 2
+   QCBOR_DECODE_MODE_MAP_AS_ARRAY = 2,
+   /**
+    * This checks that the input is encoded with preferred
+    * serialization. The checking is performed as each item is
+    * decoded. If no QCBORDecode_GetXxx() is called for an item,
+    * there's no check on that item. Preferred serialization was first
+    * defined in section 4.1 of RFC 8949, but is more sharply in
+    * draft-ietf-cbor-cde. Summarizing, the requirements are: the use
+    * of definite-length encoding only, integers, including string
+    * lengths and tags, must be in shortest form, and floating-point
+    * numbers must be reduced to shortest form all the way to
+    * half-precision. */
+   QCBOR_DECODE_MODE_PREFERRED = 3,
+
+   /** This checks that maps in the input are sorted by label as
+    * described in RFC 8949 section 4.2.1.  This also performs
+    * duplicate label checking.  This mode adds considerable CPU-time
+    * expense to decoding, though it is probably only of consequence
+    * for large inputs on slow CPUs.
+    *
+    * This also performs all the checks that
+    * QCBOR_DECODE_MODE_PREFERRED does. */
+   QCBOR_DECODE_MODE_CDE = 4,
+   
+   /** This requires integer-float unification. It performs all the checks that
+    * QCBOR_DECODE_MODE_CDE does. */
+   QCBOR_DECODE_MODE_DCBOR = 5
+
    /* This is stored in uint8_t in places; never add values > 255 */
 } QCBORDecodeMode;
 
@@ -554,7 +581,6 @@
     */
    uint16_t uTags[QCBOR_MAX_TAGS_PER_ITEM];
 #endif
-
 } QCBORItem;
 
 /**
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 95e0a95..a9aebff 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -358,6 +358,7 @@
  * - Max items in an array or map when encoding or decoding is
  *   @ref QCBOR_MAX_ITEMS_IN_ARRAY (typically 65,536).
  * - Does not directly support labels in maps other than text strings & integers.
+ * - Traversal, duplicate and sort order checking errors out for labels that are arrays or maps.
  * - Does not directly support integer labels beyond whats fits in @c int64_t
  *   or @c uint64_t.
  * - Epoch dates limited to @c INT64_MAX (+/- 292 billion years).
@@ -880,7 +881,7 @@
  * @param[in] dNum  The double-precision number to add.
  *
  * Output a double-precision float straight-through with no checking or
- * processing for preferred serializtion, dCBOR or other.
+ * processing for preferred serialization, dCBOR or other.
  *
  * Error handling is the same as QCBOREncode_AddInt64().
  *
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 04d0d11..0a3fb9c 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -383,6 +383,7 @@
    uint8_t  uDecodeMode;
    uint8_t  bStringAllocateAll;
    uint8_t  uLastError;  /* QCBORError stuffed into a uint8_t */
+   uint8_t  bAllowAllLabels; /* Used internally only, not an external feature yet */
 
    /* See MapTagNumber() for description of how tags are mapped. */
    uint64_t auMappedTags[QCBOR_NUM_MAPPED_TAGS];
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index 91ffda1..4177010 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -44,6 +44,7 @@
 
  when        who          what, where, why
  --------    ----         ---------------------------------------------------
+ 1/7/2024    llundblade   Add UsefulInputBuf_Compare().
  21/05/2024  llundblade   Comment formatting and some code tidiness.
  28/02/2022  llundblade   Rearrange UsefulOutBuf_Compare().
  19/11/2023  llundblade   Add UsefulOutBuf_GetOutput().
@@ -452,6 +453,41 @@
  *
  * Code Reviewers: THIS FUNCTION DOES POINTER MATH
  */
+int
+UsefulInputBuf_Compare(UsefulInputBuf *pUInBuf,
+                       const size_t    uOffset1,
+                       const size_t    uLen1,
+                       const size_t    uOffset2,
+                       const size_t    uLen2)
+{
+   UsefulBufC UB1;
+   UsefulBufC UB2;
+
+   const size_t uInputSize = UsefulInputBuf_GetBufferLength(pUInBuf);
+
+   /* Careful length check that works even if uLen1 + uOffset1 > SIZE_MAX */
+   if(uOffset1 > uInputSize || uLen1 > uInputSize - uOffset1) {
+      return 1;
+   }
+   UB1.ptr = (const uint8_t *)pUInBuf->UB.ptr + uOffset1;
+   UB1.len = uLen1;
+
+   /* Careful length check that works even if uLen2 + uOffset2 > SIZE_MAX */
+   if(uOffset2 > uInputSize || uLen2 > uInputSize - uOffset2) {
+      return -1;
+   }
+   UB2.ptr = (const uint8_t *)pUInBuf->UB.ptr + uOffset2;
+   UB2.len = uLen2;
+
+   return UsefulBuf_Compare(UB1, UB2);
+}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
 int UsefulOutBuf_Compare(UsefulOutBuf *pMe,
                          const size_t uStart1, const size_t uLen1,
                          const size_t uStart2, const size_t uLen2)
diff --git a/src/ieee754.c b/src/ieee754.c
index 69bf113..f9b7a3f 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -875,7 +875,7 @@
 
 /* Public function; see ieee754.h */
 int
-IEEE754_IsNotStandardDoubleNaN(const double d)
+IEEE754_DoubleHasNaNPayload(const double d)
 {
    const uint64_t uDouble                 = CopyDoubleToUint64(d);
    const uint64_t uDoubleBiasedExponent   = (uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT;
@@ -895,19 +895,20 @@
 
 /* Public function; see ieee754.h */
 int
-IEEE754_IsNotStandardSingleNaN(const float f)
+IEEE754_SingleHasNaNPayload(const float f)
 {
    const uint32_t uSingle                 = CopyFloatToUint32(f);
    const uint32_t uSingleBiasedExponent   = (uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT;
    /* Cast safe because of mask above; exponents < SINGLE_EXPONENT_MAX */
    const int32_t  nSingleUnbiasedExponent = (int32_t)uSingleBiasedExponent - SINGLE_EXPONENT_BIAS;
-   const uint32_t uSingleleSignificand    = uSingle & SINGLE_SIGNIFICAND_MASK;
+   const uint32_t uSingleSignificand      = uSingle & SINGLE_SIGNIFICAND_MASK;
 
    if(nSingleUnbiasedExponent == SINGLE_EXPONENT_INF_OR_NAN &&
-      uSingleleSignificand != 0 &&
-      uSingleleSignificand != SINGLE_QUIET_NAN_BIT) {
+      uSingleSignificand != 0 &&
+      uSingleSignificand != SINGLE_QUIET_NAN_BIT) {
       return 1;
    } else {
       return 0;
    }
 }
+
diff --git a/src/ieee754.h b/src/ieee754.h
index c893e6f..c82b9b4 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -223,7 +223,7 @@
  * represent a NaN.
  */
 int
-IEEE754_IsNotStandardDoubleNaN(double dNum);
+IEEE754_DoubleHasNaNPayload(double dNum);
 
 
 
@@ -234,11 +234,11 @@
  *
  * @returns 0 if a quiet NaN, 1 if it has a payload.
  *
- * See IEEE754_IsNotStandardDoubleNaN(). A single precision quiet NaN
+ * See IEEE754_DoubleHasNaNPayload(). A single precision quiet NaN
  * is 0x7fc00000.
  */
 int
-IEEE754_IsNotStandardSingleNaN(float fNum);
+IEEE754_SingleHasNaNPayload(float fNum);
 
 
 #endif /* ieee754_h */
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index eabd3e4..05363f0 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -773,6 +773,7 @@
  * @brief Decode the CBOR head, the type and argument.
  *
  * @param[in] pUInBuf            The input buffer to read from.
+ * @param[in] bRequirePreferred  Require preferred serialization for argument.
  * @param[out] pnMajorType       The decoded major type.
  * @param[out] puArgument        The decoded argument.
  * @param[out] pnAdditionalInfo  The decoded Lower 5 bits of initial byte.
@@ -781,11 +782,10 @@
  * @retval QCBOR_ERR_HIT_END Unexpected end of input
  *
  * This decodes the CBOR "head" that every CBOR data item has. See
- * longer explaination of the head in documentation for
- * QCBOREncode_EncodeHead().
+ * longer description in QCBOREncode_EncodeHead().
  *
- * This does the network->host byte order conversion. The conversion
- * here also results in the conversion for floats in addition to that
+ * This does the network to host byte order conversion. The conversion
+ * here also provides the conversion for floats in addition to that
  * for lengths, tags and integer values.
  *
  * The int type is preferred to uint8_t for some variables as this
@@ -794,21 +794,21 @@
  */
 static QCBORError
 QCBOR_Private_DecodeHead(UsefulInputBuf *pUInBuf,
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+                         bool            bRequirePreferred,
+#endif
                          int            *pnMajorType,
                          uint64_t       *puArgument,
                          int            *pnAdditionalInfo)
 {
    QCBORError uReturn;
+   uint64_t   uArgument;
 
-   /* Get the initial byte that every CBOR data item has and break it
-    * down. */
+   /* Get and break down initial byte that every CBOR data item has */
    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.
@@ -818,14 +818,46 @@
       /* 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. */
+         /* This shift-and-add gives the endian conversion. */
          uArgument = (uArgument << 8) + UsefulInputBuf_GetByte(pUInBuf);
       }
+
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+      /* If requested, check that argument is in preferred form */
+      if(bRequirePreferred) {
+         uint64_t uMinArgument;
+
+         if(nAdditionalInfo == LEN_IS_ONE_BYTE) {
+            if(uArgument < 24) {
+               uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE;
+               goto Done;
+            }
+         } else {
+            if(nTmpMajorType != CBOR_MAJOR_TYPE_SIMPLE) {
+               /* Check only if not a floating-point number */
+               int nArgLen = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE - 1];
+               uMinArgument = UINT64_MAX >> ((int)sizeof(uint64_t) - nArgLen) * 8;
+               if(uArgument <= uMinArgument) {
+                  uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE;
+                  goto Done;
+               }
+            }
+         }
+      }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+
    } else if(nAdditionalInfo >= ADDINFO_RESERVED1 && nAdditionalInfo <= ADDINFO_RESERVED3) {
       /* The reserved and thus-far unused additional info values */
       uReturn = QCBOR_ERR_UNSUPPORTED;
       goto Done;
    } else {
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+      if(bRequirePreferred && nAdditionalInfo == LEN_IS_INDEFINITE) {
+         uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE;
+         goto Done;
+      }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+
       /* Less than 24, additional info is argument or 31, an
        * indefinite-length.  No more bytes to get.
        */
@@ -1137,6 +1169,208 @@
 }
 
 
+#if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+
+static QCBORError
+QCBORDecode_Private_HalfConformance(const double d, const uint8_t uDecodeMode)
+{
+   struct IEEE754_ToInt ToInt;
+
+   /* Only need to check for conversion to integer because
+    * half-precision is always preferred serialization. Don't
+    * need special checker for half-precision because whole
+    * numbers always convert perfectly from half to double.
+    *
+    * This catches half-precision with NaN payload too.
+    *
+    * The only thing allowed here is a double/half-precision that
+    * can't be converted to anything but a double.
+    */
+   if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+      ToInt = IEEE754_DoubleToInt(d);
+      if(ToInt.type != QCBOR_TYPE_DOUBLE) {
+         return QCBOR_ERR_DCBOR_CONFORMANCE;
+      }
+   }
+
+   return QCBOR_SUCCESS;
+}
+
+
+static QCBORError
+QCBORDecode_Private_SingleConformance(const float f, const uint8_t uDecodeMode)
+{
+   struct IEEE754_ToInt ToInt;
+   IEEE754_union        ToSmaller;
+
+   if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+      /* See if it could have been encoded as an integer */
+      ToInt = IEEE754_SingleToInt(f);
+      if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) {
+         return QCBOR_ERR_DCBOR_CONFORMANCE;
+      }
+
+      /* Make sure there is no NaN payload */
+      if(IEEE754_SingleHasNaNPayload(f)) {
+         return QCBOR_ERR_DCBOR_CONFORMANCE;
+      }
+   }
+
+   /* See if it could have been encoded shorter */
+   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+      ToSmaller = IEEE754_SingleToHalf(f, true);
+      if(ToSmaller.uSize != sizeof(float)) {
+         return QCBOR_ERR_PREFERRED_CONFORMANCE;
+      }
+   }
+
+   return QCBOR_SUCCESS;
+}
+
+
+static QCBORError
+QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode)
+{
+   struct IEEE754_ToInt ToInt;
+   IEEE754_union        ToSmaller;
+
+   if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+      /* See if it could have been encoded as an integer */
+      ToInt = IEEE754_DoubleToInt(d);
+      if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) {
+         return QCBOR_ERR_DCBOR_CONFORMANCE;
+      }
+      /* Make sure there is no NaN payload */
+      if(IEEE754_DoubleHasNaNPayload(d)) {
+         return QCBOR_ERR_DCBOR_CONFORMANCE;
+      }
+   }
+   
+   /* See if it could have been encoded shorter */
+   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+      ToSmaller = IEEE754_DoubleToSmaller(d, true, true);
+      if(ToSmaller.uSize != sizeof(double)) {
+         return QCBOR_ERR_PREFERRED_CONFORMANCE;
+      }
+   }
+
+   return QCBOR_SUCCESS;
+}
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+
+static QCBORError
+QCBORDecode_Private_SingleConformance(const float f, uint8_t uDecodeMode)
+{
+   (void)f;
+   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+      return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE;
+   } else {
+      return QCBOR_SUCCESS;
+   }
+}
+
+static QCBORError
+QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode)
+{
+   (void)d;
+   if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+      return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE;
+   } else {
+      return QCBOR_SUCCESS;
+   }
+}
+#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+
+/*
+ * Decode a float
+ */
+static QCBORError
+QCBOR_Private_DecodeFloat(const uint8_t  uDecodeMode,
+                          const int      nAdditionalInfo,
+                          const uint64_t uArgument,
+                          QCBORItem     *pDecodedItem)
+{
+   QCBORError uReturn = QCBOR_SUCCESS;
+   float      single;
+
+   (void)single; /* Avoid unused error from various #ifndefs */
+
+   switch(nAdditionalInfo) {
+      case HALF_PREC_FLOAT: /* 25 */
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+         /* Half-precision is returned as a double. The cast to
+          * uint16_t is safe because the encoded value was 16 bits. It
+          * was widened to 64 bits to be passed in here.
+          */
+         pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument);
+         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+
+         uReturn = QCBORDecode_Private_HalfConformance(pDecodedItem->val.dfnum, uDecodeMode);
+         if(uReturn != QCBOR_SUCCESS) {
+            break;
+         }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+         uReturn = FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS);
+         break;
+
+      case SINGLE_PREC_FLOAT: /* 26 */
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+         /* Single precision is normally returned as a double since
+          * double is widely supported, there is no loss of precision,
+          * it makes it easy for the caller in most cases and it can
+          * be converted back to single with no loss of precision
+          *
+          * The cast to uint32_t is safe because the encoded value was
+          * 32 bits. It was widened to 64 bits to be passed in here.
+          */
+         single = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument);
+         uReturn = QCBORDecode_Private_SingleConformance(single, uDecodeMode);
+         if(uReturn != QCBOR_SUCCESS) {
+            break;
+         }
+
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         /* In the normal case, use HW to convert float to
+          * double. */
+         pDecodedItem->val.dfnum = (double)single;
+         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+         /* Use of float HW is disabled, return as a float. */
+         pDecodedItem->val.fnum  = single;
+         pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
+
+         /* IEEE754_FloatToDouble() could be used here to return as
+          * a double, but it adds object code and most likely
+          * anyone disabling FLOAT HW use doesn't care about floats
+          * and wants to save object code.
+          */
+#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+         uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
+         break;
+
+      case DOUBLE_PREC_FLOAT: /* 27 */
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+         pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument);
+         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+
+         uReturn = QCBORDecode_Private_DoubleConformance(pDecodedItem->val.dfnum, uDecodeMode);
+         if(uReturn != QCBOR_SUCCESS) {
+            break;
+         }
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+         uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
+         break;
+   }
+
+   return uReturn;
+}
+
+
 /* Make sure #define value line up as DecodeSimple counts on this. */
 #if QCBOR_TYPE_FALSE != CBOR_SIMPLEV_FALSE
 #error QCBOR_TYPE_FALSE macro value wrong
@@ -1166,7 +1400,6 @@
 #error QCBOR_TYPE_FLOAT macro value wrong
 #endif
 
-
 /**
  * @brief Decode major type 7 -- true, false, floating-point, break...
  *
@@ -1182,7 +1415,8 @@
  *                                           type in input.
  */
 static QCBORError
-QCBOR_Private_DecodeType7(const int      nAdditionalInfo,
+QCBOR_Private_DecodeType7(const uint8_t  uDecodeMode,
+                          const int      nAdditionalInfo,
                           const uint64_t uArgument,
                           QCBORItem     *pDecodedItem)
 {
@@ -1201,55 +1435,9 @@
        */
 
       case HALF_PREC_FLOAT: /* 25 */
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
-         /* Half-precision is returned as a double.  The cast to
-          * uint16_t is safe because the encoded value was 16 bits. It
-          * was widened to 64 bits to be passed in here.
-          */
-         pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument);
-         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
-#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
-         uReturn = FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS);
-         break;
       case SINGLE_PREC_FLOAT: /* 26 */
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-         /* Single precision is normally returned as a double since
-          * double is widely supported, there is no loss of precision,
-          * it makes it easy for the caller in most cases and it can
-          * be converted back to single with no loss of precision
-          *
-          * The cast to uint32_t is safe because the encoded value was
-          * 32 bits. It was widened to 64 bits to be passed in here.
-          */
-         {
-            const float f = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument);
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
-            /* In the normal case, use HW to convert float to
-             * double. */
-            pDecodedItem->val.dfnum = (double)f;
-            pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
-            /* Use of float HW is disabled, return as a float. */
-            pDecodedItem->val.fnum = f;
-            pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
-
-            /* IEEE754_FloatToDouble() could be used here to return as
-             * a double, but it adds object code and most likely
-             * anyone disabling FLOAT HW use doesn't care about floats
-             * and wants to save object code.
-             */
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
-         }
-#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
-         uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
-         break;
-
       case DOUBLE_PREC_FLOAT: /* 27 */
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-         pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument);
-         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
-#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
-         uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
+         uReturn = QCBOR_Private_DecodeFloat(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
          break;
 
       case CBOR_SIMPLEV_FALSE: /* 20 */
@@ -1257,12 +1445,19 @@
       case CBOR_SIMPLEV_NULL:  /* 22 */
       case CBOR_SIMPLEV_UNDEF: /* 23 */
       case CBOR_SIMPLE_BREAK:  /* 31 */
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+         if(uDecodeMode >= QCBOR_ENCODE_MODE_DCBOR &&
+            nAdditionalInfo == CBOR_SIMPLEV_UNDEF) {
+            uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
+            goto Done;
+         }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
          break; /* nothing to do */
 
       case CBOR_SIMPLEV_ONEBYTE: /* 24 */
          if(uArgument <= CBOR_SIMPLE_BREAK) {
             /* This takes out f8 00 ... f8 1f which should be encoded
-             * as e0 … f7
+             * as e0 … f7 -- preferred serialization check for simple values.
              */
             uReturn = QCBOR_ERR_BAD_TYPE_7;
             goto Done;
@@ -1270,8 +1465,16 @@
          /* FALLTHROUGH */
 
       default: /* 0-19 */
-         pDecodedItem->uDataType   = QCBOR_TYPE_UKNOWN_SIMPLE;
-         /* DecodeHead() will make uArgument equal to
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+         if(uDecodeMode >= QCBOR_ENCODE_MODE_DCBOR &&
+            (uArgument < CBOR_SIMPLEV_FALSE || uArgument > CBOR_SIMPLEV_NULL)) {
+            uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
+            goto Done;
+         }
+#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */
+
+         pDecodedItem->uDataType = QCBOR_TYPE_UKNOWN_SIMPLE;
+         /* QCBOR_Private_DecodeHead() will make uArgument equal to
           * nAdditionalInfo when nAdditionalInfo is < 24. This cast is
           * safe because the 2, 4 and 8 byte lengths of uNumber are in
           * the double/float cases above
@@ -1328,7 +1531,14 @@
    /* Decode the "head" that every CBOR item has into the major type,
     * argument and the additional info.
     */
-   uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf), &nMajorType, &uArgument, &nAdditionalInfo);
+   uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf),
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+                                      // TODO: make this prettier; will optimizer take out stuff without ifdef?
+                                      pMe->uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED,
+#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */
+                                      &nMajorType,
+                                      &uArgument,
+                                      &nAdditionalInfo);
    if(uReturn != QCBOR_SUCCESS) {
       return uReturn;
    }
@@ -1361,7 +1571,7 @@
 
       case CBOR_MAJOR_TYPE_SIMPLE:
          /* Major type 7: float, double, true, false, null... */
-         return QCBOR_Private_DecodeType7(nAdditionalInfo, uArgument, pDecodedItem);
+         return QCBOR_Private_DecodeType7(pMe->uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
          break;
 
       default:
@@ -1442,7 +1652,6 @@
       goto Done;
    }
 
-
    /* This is where out-of-place break is detected for the whole
     * decoding stack. Break is an error for everything that calls
     * QCBORDecode_Private_GetNextFullString(), so the check is
@@ -1762,13 +1971,13 @@
  * This also implements maps-as-array mode where a map is treated like
  * an array to allow caller to do their own label processing.
  */
-
 static QCBORError
 QCBORDecode_Private_GetNextMapEntry(QCBORDecodeContext *pMe,
-                                    QCBORItem          *pDecodedItem)
+                                    QCBORItem          *pDecodedItem,
+                                    uint32_t           *puLabelEndOffset)
 {
    QCBORItem  LabelItem;
-   QCBORError uErr;
+   QCBORError uErr, uErr2;
 
    uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
    if(QCBORDecode_IsUnrecoverableError(uErr)) {
@@ -1786,12 +1995,22 @@
 
    /* Decoding a map entry, so the item decoded above was the label */
    LabelItem = *pDecodedItem;
+   
+   if(puLabelEndOffset != NULL) {
+       /* Cast is OK because lengths are all 32-bit in QCBOR */
+       *puLabelEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+    }
 
    /* Get the value of the map item */
-   uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
-   if(QCBORDecode_IsUnrecoverableError(uErr)) {
+   uErr2 = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
+   if(QCBORDecode_IsUnrecoverableError(uErr2)) {
+      uErr = uErr2;
       goto Done;
    }
+   if(uErr2 != QCBOR_SUCCESS) {
+      /* The recoverable error for the value overrides the recoverable error for the label, if there was an error for the label */
+      uErr = uErr2;
+   }
 
    /* Combine the label item and value item into one */
    pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
@@ -1825,8 +2044,15 @@
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 
       default:
-         uErr = QCBOR_ERR_MAP_LABEL_TYPE;
-         goto Done;
+         /* It is possible to skip over labels that are non-aggregate
+          * types like floats, but not to skip over labels that are
+          * arrays or maps. We might eventually handle more label
+          * types like floats as they are not too hard and we now
+          * have QCBOR_DISABLE_NON_INTEGER_LABELS */
+         if(!pMe->bAllowAllLabels || QCBORItem_IsMapOrArray(LabelItem)) {
+            uErr = QCBOR_ERR_MAP_LABEL_TYPE;
+            goto Done;
+         }
    }
 
 Done:
@@ -2024,7 +2250,8 @@
 static QCBORError
 QCBORDecode_Private_GetNextMapOrArray(QCBORDecodeContext *pMe,
                                       bool               *pbBreak,
-                                      QCBORItem          *pDecodedItem)
+                                      QCBORItem          *pDecodedItem,
+                                      uint32_t           *puLabelEndOffset)
 {
    QCBORError uReturn;
    /* ==== First: figure out if at the end of a traversal ==== */
@@ -2052,7 +2279,7 @@
    }
 
    /* ==== Next: not at the end, so get another item ==== */
-   uReturn = QCBORDecode_Private_GetNextMapEntry(pMe, pDecodedItem);
+   uReturn = QCBORDecode_Private_GetNextMapEntry(pMe, pDecodedItem, puLabelEndOffset);
    if(QCBORDecode_IsUnrecoverableError(uReturn)) {
       /* Error is so bad that traversal is not possible. */
       goto Done;
@@ -2624,7 +2851,7 @@
 {
    QCBORError uReturn;
 
-   uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem);
+   uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem, NULL);
    if(uReturn != QCBOR_SUCCESS) {
       goto Done;
    }
@@ -2700,6 +2927,223 @@
 }
 
 
+/**
+ * @brief Consume an entire map or array including its contents.
+ *
+ * @param[in]  pMe              The decoder context.
+ * @param[in]  pItemToConsume   The array/map whose contents are to be
+ *                              consumed.
+ * @param[out] puNextNestLevel  The next nesting level after the item was
+ *                              fully consumed.
+ *
+ * This may be called when @c pItemToConsume is not an array or
+ * map. In that case, this is just a pass through for @c puNextNestLevel
+ * since there is nothing to do.
+ */
+static QCBORError
+QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe,
+                                const QCBORItem    *pItemToConsume,
+                                bool               *pbBreak,
+                                uint8_t            *puNextNestLevel)
+{
+   QCBORError uReturn;
+   QCBORItem  Item;
+
+   /* If it is a map or array, this will tell if it is empty. */
+   const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel);
+
+   if(QCBORItem_IsMapOrArray(*pItemToConsume) && !bIsEmpty) {
+      /* There is only real work to do for non-empty maps and arrays */
+
+      /* This works for definite- and indefinite-length maps and
+       * arrays by using the nesting level
+       */
+      do {
+         uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, pbBreak, &Item, NULL);
+         if(QCBORDecode_IsUnrecoverableError(uReturn) ||
+            uReturn == QCBOR_ERR_NO_MORE_ITEMS) {
+            goto Done;
+         }
+      } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
+
+      *puNextNestLevel = Item.uNextNestLevel;
+
+      uReturn = QCBOR_SUCCESS;
+
+   } else {
+      /* pItemToConsume is not a map or array. Just pass the nesting
+       * level through. */
+      *puNextNestLevel = pItemToConsume->uNextNestLevel;
+
+      uReturn = QCBOR_SUCCESS;
+   }
+
+Done:
+    return uReturn;
+}
+
+
+/*
+ *
+ * This consumes the next item. It returns the starting position of
+ * the label and the length of the label. It also returns the nest
+ * level of the item consumed.
+ */
+static QCBORError
+QCBORDecode_Private_GetLabelAndConsume(QCBORDecodeContext *pMe,
+                                       uint8_t            *puNestLevel,
+                                       size_t             *puLabelStart,
+                                       size_t             *puLabelLen)
+{
+   QCBORError uErr;
+   QCBORItem  Item;
+   uint8_t    uLevel;
+   uint32_t   uLabelOffset;
+
+   /* Get the label and consume it should it be complex */
+   *puLabelStart = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+   uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, &Item, &uLabelOffset);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+   *puLabelLen = uLabelOffset - *puLabelStart;
+   *puNestLevel = Item.uNestingLevel;
+   uErr = QCBORDecode_Private_ConsumeItem(pMe, &Item, NULL, &uLevel);
+
+Done:
+   return uErr;
+}
+
+
+/* Loop over items in a map until the end of the map looking for
+ * duplicates. This starts at the current position in the map, not at
+ * the beginning of the map.
+ *
+ * This saves and restores the traversal cursor and nest tracking so
+ * they are the same on exit as they were on entry.
+ */
+static QCBORError
+QCBORDecode_Private_CheckDups(QCBORDecodeContext *pMe,
+                              const uint8_t       uNestLevel,
+                              const size_t        uCompareLabelStart,
+                              const size_t        uCompareLabelLen)
+{
+   QCBORError uErr;
+   size_t     uLabelStart;
+   size_t     uLabelLen;
+   uint8_t    uLevel;
+   int        nCompare;
+
+   const QCBORDecodeNesting SaveNesting = pMe->nesting;
+   const UsefulInputBuf     Save        = pMe->InBuf;
+
+   do {
+      uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uLevel, &uLabelStart, &uLabelLen);
+      if(uErr != QCBOR_SUCCESS) {
+         if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
+            uErr = QCBOR_SUCCESS; /* Successful end */
+         }
+         break;
+      }
+
+      if(uLevel != uNestLevel) {
+         break; /* Successful end of loop */
+      }
+
+      /* This check for dups works for labels that are preferred
+       * serialization and are not maps. If the labels are not in
+       * preferred serialization, then the check has to be more
+       * complicated and is type-specific because it uses the decoded
+       * value, not the encoded CBOR. It is further complicated for
+       * maps because the order of items in a map that is a label
+       * doesn't matter when checking that is is the duplicate of
+       * another map that is a label. QCBOR so far only turns on this
+       * dup checking as part of CDE checking which requires preferred
+       * serialization.  See 5.6 in RFC 8949.
+       */
+      nCompare = UsefulInputBuf_Compare(&(pMe->InBuf),
+                                         uCompareLabelStart, uCompareLabelLen,
+                                         uLabelStart, uLabelLen);
+      if(nCompare == 0) {
+         uErr = QCBOR_ERR_DUPLICATE_LABEL;
+         break;
+      }
+   } while (1);
+
+   pMe->nesting = SaveNesting;
+   pMe->InBuf   = Save;
+
+   return uErr;
+}
+
+
+/* This does sort order and duplicate detection on a map. The and all
+ * it's members must be in preferred serialization so the comparisons
+ * work correctly.
+ */
+static QCBORError
+QCBORDecode_Private_CheckMap(QCBORDecodeContext *pMe, const QCBORItem *pMapToCheck)
+{
+   QCBORError uErr;
+   uint8_t    uNestLevel;
+   size_t     offset2, offset1, length2, length1;
+
+   const QCBORDecodeNesting SaveNesting = pMe->nesting;
+   const UsefulInputBuf Save = pMe->InBuf;
+   pMe->bAllowAllLabels = 1;
+
+   /* This loop runs over all the items in the map once, comparing
+    * each adjacent pair for correct ordering. It also calls CheckDup
+    * on each one which also runs over the remaining items in the map
+    * checking for duplicates. So duplicate checking runs in n^2.
+    */
+
+   offset2 = SIZE_MAX;
+   length2 = SIZE_MAX; // To avoid uninitialized warning
+   while(1) {
+      uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uNestLevel, &offset1, &length1);
+      if(uErr != QCBOR_SUCCESS) {
+         if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
+            uErr = QCBOR_SUCCESS; /* Successful exit from loop */
+         }
+         break;
+      }
+
+      if(uNestLevel < pMapToCheck->uNextNestLevel) {
+         break; /* Successful exit from loop */
+      }
+
+      if(offset2 != SIZE_MAX) {
+         /* Check that the labels are ordered. Check is not done the
+          * first time through the loop when offset2 is unset. Since
+          * this does comparison of the items in encoded form they
+          * must be preferred serialization encoded. See RFC 8949
+          * 4.2.1.
+          */
+         if(UsefulInputBuf_Compare(&(pMe->InBuf), offset2, length2, offset1, length1) > 0) {
+            uErr = QCBOR_ERR_UNSORTED;
+            break;
+         }
+      }
+
+      uErr = QCBORDecode_Private_CheckDups(pMe, pMapToCheck->uNextNestLevel, offset1, length1);
+      if(uErr != QCBOR_SUCCESS) {
+         break;
+      }
+
+      offset2 = offset1;
+      length2 = length1;
+   }
+
+   pMe->bAllowAllLabels = 0;
+   pMe->nesting = SaveNesting;
+   pMe->InBuf = Save;
+
+   return uErr;
+}
+
+
 /*
  * Public function, see header qcbor/qcbor_decode.h file
  */
@@ -2707,7 +3151,13 @@
 QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
 {
    QCBORError uErr;
-   uErr =  QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem);
+   uErr = QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem);
+#ifndef QCBOR_DISABLE_CONFORMANCE
+   if(uErr == QCBOR_SUCCESS && pMe->uDecodeMode >= QCBOR_ENCODE_MODE_CDE && pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
+      /* Traverse map checking sort order and for duplicates */
+      uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem);
+   }
+#endif /* ! QCBOR_DISABLE_CONFORMANCE */
    if(uErr != QCBOR_SUCCESS) {
       pDecodedItem->uDataType  = QCBOR_TYPE_NONE;
       pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
@@ -3143,63 +3593,6 @@
 
 
 
-
-/**
- * @brief Consume an entire map or array including its contents.
- *
- * @param[in]  pMe              The decoder context.
- * @param[in]  pItemToConsume   The array/map whose contents are to be
- *                              consumed.
- * @param[out] puNextNestLevel  The next nesting level after the item was
- *                              fully consumed.
- *
- * This may be called when @c pItemToConsume is not an array or
- * map. In that case, this is just a pass through for @c puNextNestLevel
- * since there is nothing to do.
- */
-static QCBORError
-QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe,
-                                const QCBORItem    *pItemToConsume,
-                                bool               *pbBreak,
-                                uint8_t            *puNextNestLevel)
-{
-   QCBORError uReturn;
-   QCBORItem  Item;
-
-   /* If it is a map or array, this will tell if it is empty. */
-   const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel);
-
-   if(QCBORItem_IsMapOrArray(*pItemToConsume) && !bIsEmpty) {
-      /* There is only real work to do for non-empty maps and arrays */
-
-      /* This works for definite- and indefinite-length maps and
-       * arrays by using the nesting level
-       */
-      do {
-         uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, pbBreak, &Item);
-         if(QCBORDecode_IsUnrecoverableError(uReturn) ||
-            uReturn == QCBOR_ERR_NO_MORE_ITEMS) {
-            goto Done;
-         }
-      } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
-
-      *puNextNestLevel = Item.uNextNestLevel;
-
-      uReturn = QCBOR_SUCCESS;
-
-   } else {
-      /* pItemToConsume is not a map or array. Just pass the nesting
-       * level through. */
-      *puNextNestLevel = pItemToConsume->uNextNestLevel;
-
-      uReturn = QCBOR_SUCCESS;
-   }
-
-Done:
-    return uReturn;
-}
-
-
 /*
  * Public function, see header qcbor/qcbor_decode.h file
  */
@@ -3648,8 +4041,8 @@
    bInMap = DecodeNesting_IsCurrentTypeMap(&(pMe->nesting));
 
    /* Could call GetNext here, but don't need to because this
-    * is only interested in arrays and maps. */
-   uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem);
+    * is only interested in arrays and maps. TODO: switch to GetNext()? */
+   uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem, NULL);
    if(uErr != QCBOR_SUCCESS) {
       pMe->uLastError = (uint8_t)uErr;
       return;
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index e54f03e..7d2f7fb 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -681,7 +681,7 @@
    struct IEEE754_ToInt IntResult;
 
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(IEEE754_IsNotStandardDoubleNaN(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+   if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
       pMe->uError = QCBOR_ERR_NOT_ALLOWED;
       return;
    }
@@ -730,7 +730,7 @@
    struct IEEE754_ToInt IntResult;
 
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(IEEE754_IsNotStandardSingleNaN(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+   if(IEEE754_SingleHasNaNPayload(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
       pMe->uError = QCBOR_ERR_NOT_ALLOWED;
       return;
    }
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index 0235466..6e47f3c 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -888,6 +888,31 @@
       return "UIB SetBufferLength failed";
    }
 
+   UsefulBufC CompCheck = UsefulBuf_FROM_SZ_LITERAL("abcd");
+   UsefulInputBuf_Init(&UIB, CompCheck);
+
+   if(UsefulInputBuf_Compare(&UIB, 0, 2, 2, 2) >= 0) {
+      return "UB 1 compared greater than UB2";
+   }
+   if(UsefulInputBuf_Compare(&UIB, 0, 2, 0, 2) != 0) {
+      return "UB1 and UB2 didn't compare equally";
+   }
+   if(UsefulInputBuf_Compare(&UIB, 2, 2, 0, 2) <= 0) {
+      return "UB2 compared less than UB1";
+   }
+   if(UsefulInputBuf_Compare(&UIB, 4, 1, 2, 2) <= 0) {
+      return "Off-the-end UB1 compared as less than UB2";
+   }
+   if(UsefulInputBuf_Compare(&UIB, 0, 5, 2, 2) <= 0) {
+      return "Off-the-end UB1 compared as less than UB2 (second)";
+   }
+   if(UsefulInputBuf_Compare(&UIB, 0, 2, 5, 1) >= 0) {
+      return "Off-the-end UB2 compared as less than UB2";
+   }
+   if(UsefulInputBuf_Compare(&UIB, 0, 2, 2, 3) >= 0) {
+      return "Off-the-end UB2 compared as less than UB2 (second)";
+   }
+
    return NULL;
 }
 
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 14bfb53..191c448 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2558,7 +2558,11 @@
       }
 #endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
 
-      if(nIndex == 4) {
+      if(nIndex == 54) {
+         uCBORError = 9; /* For setting break points */
+      }
+
+      if(strncmp("map with map label with non-preferred part", pF->szDescription, 25) == 0) {
          uCBORError = 9; /* For setting break points */
       }
 
@@ -2570,7 +2574,7 @@
          uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
       } while(uCBORError == QCBOR_SUCCESS);
 
-      /* Must get the expected error or the this test fails.
+      /* Must get the expected error or the test fails.
        * The data and label type must also be QCBOR_TYPE_NONE.
        */
       if(uCBORError != pF->nError ||
@@ -7349,8 +7353,7 @@
    },
    {
       "Decimal Fraction with positive bignum 257 * 10e3",
-      {(uint8_t[]){0xC4, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-                               0xC2, 0x42, 0x01, 0x01}, 15},
+      {(uint8_t[]){0xC4, 0x82, 0x03, 0xC2, 0x42, 0x01, 0x01}, 8},
       257000,
       EXP_AND_MANTISSA_ERROR(QCBOR_SUCCESS),
       257000,
@@ -7360,8 +7363,7 @@
    },
    {
       "bigfloat with negative bignum -258 * 2e3",
-      {(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-                               0xC3, 0x42, 0x01, 0x01}, 15},
+      {(uint8_t[]){0xC5, 0x82, 0x03, 0xC3, 0x42, 0x01, 0x01}, 8},
       -2064,
       EXP_AND_MANTISSA_ERROR(QCBOR_SUCCESS),
       0,
@@ -7371,8 +7373,7 @@
    },
    {
       "bigfloat with positive bignum 257 * 2e3",
-      {(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-                               0xC2, 0x42, 0x01, 0x01}, 15},
+      {(uint8_t[]){0xC5, 0x82, 0x03, 0xC2, 0x42, 0x01, 0x01}, 8},
       2056,
       EXP_AND_MANTISSA_ERROR(QCBOR_SUCCESS),
       2056,
@@ -9726,6 +9727,400 @@
 }
 
 
+
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+#define PREFERRED_ERR    QCBOR_ERR_PREFERRED_CONFORMANCE
+#define DCBOR_FLOAT_ERR  QCBOR_ERR_DCBOR_CONFORMANCE
+#define HALF_FLOAT_ERR   QCBOR_ERR_DCBOR_CONFORMANCE
+#else
+#define PREFERRED_ERR    QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE
+#define DCBOR_FLOAT_ERR  QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE
+#define HALF_FLOAT_ERR   QCBOR_ERR_HALF_PRECISION_DISABLED
+#endif
+#else
+#define PREFERRED_ERR    QCBOR_ERR_ALL_FLOAT_DISABLED
+#define DCBOR_FLOAT_ERR  QCBOR_ERR_ALL_FLOAT_DISABLED
+#define HALF_FLOAT_ERR   QCBOR_ERR_ALL_FLOAT_DISABLED
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+
+
+/* These are all well-formed and valid CBOR, but fail
+ * conformance with preferred, CDE or dCBOR.
+ */
+static const struct DecodeFailTestInput DecodeConformanceFailures[] = {
+   /* --- Major type 0 and 1 not shortest-form --- */
+   { "zero encoded in 2 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x18\x00", 2},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "23 encoded in 2 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x18\x17", 2},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "255 encoded in 3 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x19\x00\xff", 3},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "65535 encoded in 5 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x1a\x00\x00\xff\xff", 5},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "4294967295 encoded in 9 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x1b\x00\x00\x00\x00\xff\xff\xff\xff", 9},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "-24 encoded in 2 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x38\x17", 2},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "-256 encoded in 3 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x39\x00\xff", 3},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "-65536 encoded in 5 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x3a\x00\x00\xff\xff", 5},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "-4294967296 encoded in 9 bytes",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\x3b\x00\x00\x00\x00\xff\xff\xff\xff", 9},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   /* TODO: what to do about this test?
+    { "65-bit negative not allowed in dCBOR",
+    QCBOR_DECODE_MODE_DCBOR,
+    {"\x3b\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+    QCBOR_ERR_DCBOR_CONFORMANCE
+    },*/
+
+   /* --- Simple values not allowed in dCBOR --- */
+   { "undefined not allowed in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xf7", 1},
+      QCBOR_ERR_DCBOR_CONFORMANCE
+   },
+   { "Simple value 0 not allowed in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xe0", 1},
+      QCBOR_ERR_DCBOR_CONFORMANCE
+   },
+   { "Simple value 19 not allowed in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xf3", 1},
+      QCBOR_ERR_DCBOR_CONFORMANCE
+   },
+   { "Simple value 32 not allowed in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xF8\x20", 2},
+      QCBOR_ERR_DCBOR_CONFORMANCE
+   },
+   { "Simple value 255 not allowed in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xF8\xff", 2},
+      QCBOR_ERR_DCBOR_CONFORMANCE
+   },
+
+   /* --- Floats not in shortest-form --- */
+   { "1.5 single should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfa\x3f\xc0\x00\x00", 5},
+      PREFERRED_ERR
+   },
+   { "1.5 double should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfb\x3f\xf8\x00\x00\x00\x00\x00\x00", 9},
+      PREFERRED_ERR
+   },
+   { "8388607.0 double should be single",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xFB\x41\x5F\xFF\xFF\xC0\x00\x00\x00", 9},
+      PREFERRED_ERR
+   },
+   { "3.0517578125E-5 double should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xFB\x3F\x00\x00\x00\x00\x00\x00\x00", 9},
+      PREFERRED_ERR
+   },
+   { "255.875 single should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfa\x43\x7f\xe0\x00", 5},
+      PREFERRED_ERR
+   },
+   { "INFINITY single should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfa\x7f\x80\x00\x00", 5},
+      PREFERRED_ERR
+   },
+   { "INFINITY double should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfb\x7f\xf0\x00\x00\x00\x00\x00\x00", 9},
+      PREFERRED_ERR
+   },
+   { "-INFINITY single should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfa\xff\x80\x00\x00", 5},
+      PREFERRED_ERR
+   },
+   { "-INFINITY double should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfb\xff\xf0\x00\x00\x00\x00\x00\x00", 9},
+      PREFERRED_ERR
+   },
+   { "NAN single should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfa\x7f\xc0\x00\x00", 5},
+      PREFERRED_ERR
+   },
+   { "NAN double should be half",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00", 9},
+      PREFERRED_ERR
+   },
+   { "NAN half with payload (signaling)",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xf9\x7e\x01", 3},
+      HALF_FLOAT_ERR
+   },
+   { "NAN single with payload (signaling)",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xfa\x7f\xc0\x00\x01", 5},
+      DCBOR_FLOAT_ERR
+   },
+   { "NAN double with payload (signaling)",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xfb\x7f\xf8\x00\x00\x00\x00\x00\x01", 9},
+      DCBOR_FLOAT_ERR
+   },
+   { "NAN half with some payload",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xf9\x7e\x80", 3},
+      HALF_FLOAT_ERR
+   },
+   { "NAN single with some payload",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xfa\x7f\xc4\x00\x00", 5},
+      DCBOR_FLOAT_ERR
+   },
+   { "NAN double with some payload",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xfb\x7f\xf8\x01\x01\x00\x00\x00\x00", 9},
+      DCBOR_FLOAT_ERR
+   },
+
+   /* --- Floats that should be integers --- */
+   { "0 half not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xf9\x00\x00", 3},
+      HALF_FLOAT_ERR
+   },
+   { "0 double not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xfb\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+      DCBOR_FLOAT_ERR
+   },
+   { "-0 half not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xf9\x80\x00", 3},
+      HALF_FLOAT_ERR
+   },
+   { "18446744073709550000 double not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xFB\x43\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+      DCBOR_FLOAT_ERR
+   },
+   { "4294967295 double not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xFB\x41\xEF\xFF\xFF\xFF\xE0\x00\x00", 9},
+      DCBOR_FLOAT_ERR
+   },
+   { "65535 single not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xFA\x47\x7F\xFF\x00", 5},
+      DCBOR_FLOAT_ERR
+   },
+   { "255 half not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xF9\x5C\x00", 3},
+      HALF_FLOAT_ERR
+   },
+   { "-1 half not an integer in dCBOR",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xF9\xBC\x00", 3},
+      HALF_FLOAT_ERR
+   },
+
+   /* --- Various non-shortest-form CBOR arguments ---*/
+   { "byte string length not-shortest form",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\x59\x00\x01\x99", 4},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "array length not-shortest form",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\x9a\x00\x00\x00\x02\x05\x06", 7},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "tag number not shortest-form",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xd9\x00\xff\x00", 4},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+#if !defined(QCBOR_DISABLE_TAGS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+   { "tag number on lable not shortest-form",
+      QCBOR_DECODE_MODE_PREFERRED,
+      {"\xA3\xC1\x00\x61\x61\xD8\x01\x00\x61\x62\xD9\x00\x01\x00\x61\x63", 16},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+#endif
+
+   /* --- Indefinite lengths --- */
+   { "indefinite-length byte string",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\x5f\x62\x68\x69\xff", 5},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "indefinite-length text string",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\x7f\x62\x68\x69\xff", 5},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "indefinite-length array",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\x9f\xff", 2},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+   { "indefinite-length map",
+      QCBOR_DECODE_MODE_DCBOR,
+      {"\xbf\xff", 2},
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   },
+
+   /* --- Unsorted maps --- */
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+   { "simple unsorted map with text labels",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa2\x61\x62\x00\x61\x61\x01", 7},
+      QCBOR_ERR_UNSORTED
+   },
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+   { "reverse sorted map with integer labels",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa5\x19\x03\xE8\xf6\x18\x64\xf6\x00\xf6\x29\xf6\x3A\x00\x01\x86\x9f\xf6", 18},
+      QCBOR_ERR_UNSORTED
+   },
+   {"map with out-of-order labels that are arrays",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xA3\x83\x00\x01\x02\x61\x63\x83\x00\x01\x00\x61\x61\x83\x00\x01\x01\x61\x62", 19},
+      QCBOR_ERR_MAP_LABEL_TYPE
+   },
+#if !defined(QCBOR_DISABLE_TAGS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+   { "unsorted map with labels of all types including arrays and maps",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xA8\x80\x07\xC1\x18\x58\x02\x64\x74\x65\x78\x74\x03\x01\x01\xA0\x04"
+         "\x42\x78\x78\x05\xF5\x06\xFB\x40\x21\x8A\x3D\x70\xA3\xD7\x0A\x07", 33},
+      QCBOR_ERR_MAP_LABEL_TYPE
+   },
+   { "unsorted map with labels of all non-aggregate types",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xA6\xC1\x18\x58\x02\x64\x74\x65\x78\x74\x03\x01\x01"
+         "\x42\x78\x78\x05\xF5\x06\xFB\x40\x21\x8A\x3D\x70\xA3\xD7\x0A\x07", 29},
+      QCBOR_ERR_UNSORTED
+   },
+   {"map with out-of-order labels that have tags",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xA3\xC1\x18\x63\x61\x61\xD9\x07\xD0\x18\x63\x61\x63\xD8\x30\x18\x63\x61\x62", 19},
+      QCBOR_ERR_UNSORTED
+   },
+#endif
+
+   /* --- Maps with dup labels --- */
+   { "simple map with dup integer labels",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa2\x00\x00\x00\x00", 5},
+      QCBOR_ERR_DUPLICATE_LABEL
+   },
+   {"map with dup map labels",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xA3\xA1\x03\x03\x61\x61\xA1\x02\x02\x61\x62\xA1\x03\x03\x61\x63", 16},
+      QCBOR_ERR_MAP_LABEL_TYPE
+   },
+
+   /* --- Maps with bad labels --- */
+   { "map with invalid label",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa1\x1c\x01", 3},
+      QCBOR_ERR_UNSUPPORTED
+   },
+
+   { "map with array label with invalid parts",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa1\x81\x1c\x01", 4},
+      QCBOR_ERR_MAP_LABEL_TYPE
+   },
+
+   { "map with map label with non-preferred part",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa1\xa1\x19\x00\x00\x01\x02", 7},
+      QCBOR_ERR_MAP_LABEL_TYPE
+   }};
+
+
+static UsefulBufC CorrectlySorted[] = {
+   /* This one is correctly sorted, but is not correct preferred serialization. QCBOR checks
+    * the sort order of the map without checking the preferred serialization of the
+    * map items, so this test passes. */
+   {"\xa4\x01\x61\x61\xf9\x3C\x00\x61\x62\xFA\x3F\x80\x00\x00\x61\x63\xFB\x3F\xF0\x00\x00\x00\x00\x00\x00\x61\x64", 27},
+   {"\xa3\x00\x61\x61\x01\x61\x62\xa3\x0c\x61\x78\x0b\x61\x79\x0a\x61\x7a\x61\x63", 19},
+   {"\xA3\xE0\x61\x61\xF5\x61\x62\xFB\x3F\xF1\x99\x99\x99\x99\x99\x9A\x61\x63", 18},
+   {"\xa2\x00\x00\x01\x01", 5},
+   {"\xA0", 1},
+   NULLUsefulBufC
+};
+
+
+
+int32_t 
+DecodeConformanceTests(void)
+{
+   QCBORDecodeContext DCtx;
+   QCBORItem          Item;
+   QCBORError         uErr;
+   uint32_t           uTestIndex;
+
+   for(uTestIndex = 0; UsefulBuf_IsNULLC(CorrectlySorted[uTestIndex]); uTestIndex++) {
+      QCBORDecode_Init(&DCtx, CorrectlySorted[uTestIndex], QCBOR_DECODE_MODE_CDE);
+
+      uErr = QCBORDecode_GetNext(&DCtx, &Item);
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(1, uTestIndex, uErr);
+      }
+   }
+
+   /* Make sure EnterMap is handling errors */
+   QCBORDecode_Init(&DCtx,UsefulBuf_FROM_SZ_LITERAL("\xa2\x00\x00\x00\x00"), QCBOR_DECODE_MODE_CDE);
+   QCBORDecode_EnterMap(&DCtx, &Item);
+   if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_DUPLICATE_LABEL) {
+      return 5000;
+   }
+
+   return ProcessDecodeFailures(DecodeConformanceFailures,
+                                C_ARRAY_COUNT(DecodeConformanceFailures, struct DecodeFailTestInput));
+
+}
+
+
+
+
+
 #if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
 
 struct PreciseNumberConversion {
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 4c4c0a6..a6d5837 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -1,6 +1,6 @@
 /*==============================================================================
  Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2024, Laurence Lundblade.
  All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -319,6 +319,11 @@
 int32_t CBORTestIssue134(void);
 
 
+/*
+ * Test the decode checking features for dCBOR, CDE and preferred.
+ */
+int32_t DecodeConformanceTests(void);
+
 int32_t PreciseNumbersTest(void);
 
 int32_t ErrorHandlingTests(void);
diff --git a/test/run_tests.c b/test/run_tests.c
index e24506a..3fe4905 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -67,10 +67,18 @@
 
 
 static test_entry s_tests[] = {
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+   TEST_ENTRY(DecodeConformanceTests),
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+   TEST_ENTRY(ErrorHandlingTests),
+   TEST_ENTRY(OpenCloseBytesTest),
 #ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
    TEST_ENTRY(GetMapAndArrayTest),
    TEST_ENTRY(TellTests),
    TEST_ENTRY(ParseMapAsArrayTest),
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   TEST_ENTRY(ArrayNestingTest3),
+#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
    TEST_ENTRY(SpiffyDateDecodeTest),
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
    TEST_ENTRY(ErrorHandlingTests),
@@ -86,7 +94,7 @@
    TEST_ENTRY(IndefiniteLengthNestTest),
    TEST_ENTRY(IndefiniteLengthArrayMapTest),
    TEST_ENTRY(NestedMapTestIndefLen),
-#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
 
    TEST_ENTRY(SimpleValueDecodeTests),
    TEST_ENTRY(DecodeFailureTests),
@@ -95,11 +103,6 @@
    TEST_ENTRY(MapEncodeTest),
    TEST_ENTRY(ArrayNestingTest1),
    TEST_ENTRY(ArrayNestingTest2),
-
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   TEST_ENTRY(ArrayNestingTest3),
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-
    TEST_ENTRY(EncodeDateTest),
    TEST_ENTRY(SimpleValuesTest1),
    TEST_ENTRY(IntegerValuesTest1),
@@ -165,8 +168,6 @@
    TEST_ENTRY(PeekAndRewindTest),
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 
-
-
 #ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
    TEST_ENTRY(ExponentAndMantissaDecodeTests),
 #ifndef QCBOR_DISABLE_TAGS