Redesign of the configuration methods for encoder conformance

Encoder conformance can be ala carte or full options for preferred, CDE or dCBOR



* Rework encode conformance configuration

* Check in before merge

* tests are passing

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 4826769..affdd9b 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -432,6 +432,25 @@
     *  occurred in the decode input. */
    QCBOR_ERR_TAGS_DISABLED = 51,
 
+   // TODO: maybe reduce number of conformance codes to not use up unrecoverable errors
+   
+   /** 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 = 52,
+
+   /** 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 = 53,
+
+   /** 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 = 54,
+
+   /** A map is unsorted and should be for CDE or dCBOR. */
+   QCBOR_ERR_UNSORTED = 55,
+
+   /** Conformance checking requested, preferred serialization disabled, float in the input. */
+   QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE = 56,
+
 #define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 59
 
    /** More than @ref QCBOR_MAX_TAGS_PER_ITEM tags encountered for a
@@ -530,7 +549,7 @@
    /** Attempt to output non-preferred, non-CDE or non-dCBOR when not
     * allowed by mode. See QCBOREncode_SerializationPreferred(),
     * QCBOREncode_SerializationCDE(),
-    * QCBOREncode_SerializationdCBOR().
+    * QCBOREncode_SerializationdCBOR() and @ref QCBOR_ENCODE_CONFIG_DISALLOW_NON_PREFERRED_NUMBERS.
     */
    QCBOR_ERR_NOT_PREFERRED = 79,
 
@@ -544,23 +563,6 @@
     * 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,
-
    /* Can't output a negative zero big num */
    QCBOR_ERR_NO_NEGATIVE_ZERO = 87,
 
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 075b8f5..df365fe 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -984,8 +984,7 @@
  *   configured to handle indefinite-length strings as other than
  *   that, this implementation does no dynamic memory allocation.
  *
- * | Error | Description |
- * | ---- | ---- |
+ * x
  * | __Not well-formed errors__  ||
  * | @ref QCBOR_ERR_HIT_END                 | Partial data item; need more input bytes to complete decoding |
  * | @ref QCBOR_ERR_UNSUPPORTED             | Input contains CBOR with reserved additional info values |
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 25b67e7..d1f9d03 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -404,6 +404,155 @@
 #define QCBOR_ENCODE_AS_BORROWED 1
 
 
+
+/**
+ * This causes maps to be sorted per RFC 8949 section 4.2.1 .
+ * QCBOREncode_CloseMap() becomes equivalent to
+ * QCBOREncode_CloseAndSortMap(). This
+ * causes map closing to run much slower, but this is probably only of consequence in very
+ * constrained environments with large maps.
+ *
+ * Note that map sorting causese about 30% more code from
+ * the QCBOR library to be linked. Any call to QCBOREncode_Config(), even if sorting
+ * is not selected, will cause the sorting code to be linked.
+ * See QCBOREncode_ConfigReduced() to avoid this.
+ */
+#define QCBOR_ENCODE_CONFIG_SORT                           0x01
+
+/** By default QCBOR will error out when trying to encode
+ * a double or float NaN that has a payload because NaN
+ * payloads are not very interoperable. With this set,
+ * NaN payloads can be encoded.
+ *
+ */
+#define QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD              0x02
+
+/**
+ * This unifies the integer and floating-point number space such
+ * that there is only one way to encode any particular
+ * value. For example, 0 is always encoded as a type 0
+ * positive integer, never as a 0.0 as a float or double. This
+ * unification never loses precision. For example, 1.000001 would
+ * not be reduced to the integer 1.
+ *
+ * This specification for this reduction comes from
+ * dCBOR. It is part of a deterministic encoding that
+ * that covers integer and float numbers.  This reduction
+ * doesn't cover other number representations like
+ * big numbers and big floats.
+ */
+#define QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION                0x04
+
+/* With this set, attempts to encode indefinite length
+ * text and byte strings, arrays and maps  will error out. */
+#define QCBOR_ENCODE_CONFIG_DISALLOW_INDEFINITE_LENGTHS    0x08
+
+/* This disallows non-preferred floating number encoding, QCBOREncode_AddFloatNoPreferred()
+and QCBOREncode_AddDoubleNoPreferred().  It is not possible to disable
+preferred serialization of type 0 and type 1 integers in QCBOR. */
+#define QCBOR_ENCODE_CONFIG_DISALLOW_NON_PREFERRED_NUMBERS  0x10
+
+/**
+ * This enforces a simple rule in dCBOR allows only the simple values true, false and null.
+ * With this set, any other simple value will error out.
+ */
+#define QCBOR_ENCODE_CONFIG_ONLY_DCBOR_SIMPLE              0x20
+
+/* Preferred serialization requires number reduction
+ * of big numbers to type 0 and 1 integers. With this set
+ * an error will be set when trying to encode non-preferred
+ * big numbers with QCBOREncode_AddTBigNumberNoPreferred()
+ or QCBOREncode_AddTBigNumberRaw(). */
+#define QCBOR_ENCODE_CONFIG_ONLY_PREFERRED_BIG_NUMBERS     0x40 // TODO: test this one
+
+
+/**
+ * Setting this mode will cause QCBOR to return an error if an attempt
+ * is made to use one of the methods that produce non-preferred
+ * serialization. It doesn't change anything else as QCBOR produces
+ * preferred serialization by default.
+ *
+ * The non-preferred methods are: QCBOREncode_AddFloatNoPreferred(),
+ * QCBOREncode_AddDoubleNoPreferred(),
+ * QCBOREncode_OpenArrayIndefiniteLength(),
+ * QCBOREncode_CloseArrayIndefiniteLength(),
+ * QCBOREncode_OpenMapIndefiniteLength(),
+ * QCBOREncode_CloseMapIndefiniteLength(), plus those derived from the
+ * above listed.
+ *
+ * This mode is just a user guard to prevent accidentally calling
+ * something that produces non-preferred serialization. It doesn't do
+ * anything but causes errors to occur on attempts to call the above
+ * listed functions. This does nothing if the library is compiled
+ * QCBOR_DISABLE_ENCODE_USAGE_GUARDS.
+ *
+ * See @ref Serialization. It is usually not necessary to set this
+ * mode, but there is usually no disadvantage to setting it. Preferred
+ * serialization is defined in RFC 8949, section 4.1.
+ */
+#define QCBOR_ENCODE_CONFIG_PREFERRED \
+   (QCBOR_ENCODE_CONFIG_DISALLOW_INDEFINITE_LENGTHS | \
+    QCBOR_ENCODE_CONFIG_DISALLOW_NON_PREFERRED_NUMBERS | \
+    QCBOR_ENCODE_CONFIG_ONLY_PREFERRED_BIG_NUMBERS)
+
+
+/**
+ * This causes QCBOR to produce CBOR Deterministic Encoding (CDE).
+ * With CDE, two distant unrelated CBOR encoders will produce exactly
+ * the same encoded CBOR for a given input.
+ *
+ * In addition to doing everything
+ * @ref QCBOR_ENCODE_CONFIG_PREFERRED does (including exclusion of
+ * indefinite lengths), this causes maps to be sorted. The map is
+ * sorted automatically when QCBOREncode_CloseMap() is called. See
+ * @ref QCBOR_ENCODE_CONFIG_SORT.
+ *
+ * See @ref Serialization. It is usually not necessary to set this
+ * mode as determinism is very rarely needed. However it will
+ * usually work with most protocols. CDE is defined in
+ * draft-ietf-cbor-cde and/or RFC 8949 section 4.2.
+ */
+#define QCBOR_ENCODE_CONFIG_CDE \
+   (QCBOR_ENCODE_CONFIG_PREFERRED | \
+    QCBOR_ENCODE_CONFIG_SORT)
+
+
+/**
+ * See draft-mcnally-deterministic-cbor.
+ *
+ * This is a superset of CDE. This function does everything
+ * QCBOREncode_SerializationCDE() does. Also it is a super set of
+ * preferred serialization and does everything
+ * QCBOREncode_SerializationPreferred() does.
+ *
+ * The main feature of dCBOR is that there is only one way to serialize a
+ * particular numeric value. This changes the behavior of functions
+ * that add floating-point numbers.  If the floating-point number is
+ * whole, it will be encoded as an integer, not a floating-point number.
+ * 0.000 will be encoded as 0x00. Precision is never lost in this
+ * conversion.
+ *
+ * dCBOR also disallows NaN payloads. QCBOR will allow NaN payloads if
+ * you pass a NaN to one of the floating-point encoding functions.
+ * This mode forces all NaNs to the half-precision queit NaN.
+ *
+ * TODO: confirm and test NaN payload behavior dCBOR reduces all NaN payloads to half-precision quiet NaN
+ *
+ * dCBOR disallows use of any simple type other than true, false and
+ * NULL. In particular it disallows use of "undef" produced by
+ * QCBOREncode_AddUndef().
+ *
+ * See @ref Serialization. Set this mode only if the protocol you are
+ * implementing requires dCBOR. This mode is usually not compatible
+ * with protocols that don't use dCBOR. dCBOR is defined in
+ * draft-mcnally-deterministic-cbor.
+ */
+#define QCBOR_ENCODE_CONFIG_DCBOR \
+   (QCBOR_ENCODE_CONFIG_CDE | \
+    QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION | \
+    QCBOR_ENCODE_CONFIG_ONLY_DCBOR_SIMPLE)
+
+
 /**
  * QCBOREncodeContext is the data type that holds context for all the
  * encoding functions. It is less than 200 bytes, so it can go on the
@@ -473,165 +622,48 @@
 QCBOREncode_Init(QCBOREncodeContext *pCtx, UsefulBuf Storage);
 
 
-/* TODO: allow mix-and-match serialization behaviors
- - Sort maps
- - Reduce floats to integers
- - Restrict non-preferred big nums
- - Restrict indefinite-length strings
- - Restrict indefinite-length maps and arrays
- - Restrict simple values
- - Dup detection
- - Restrict NaN payload
-
- Easy to do by changing uMode into a bit map.
-
- Same for decode conformance check.
- */
-
-
 /**
- * @brief Select preferred serialization mode.
+ * @brief Configure the encoder.
  *
  * @param[in] pCtx   The encoding context for mode set.
+ * @param[in] uConfig  Bit flags for configuration options.
  *
- * This resests to the default serialization behaviors, that
- * allows non-preferred serialization methods to be called,
- * doesn't sort maps, and doesn't reduce whole-number floats
- * to integer.
+ * QCBOR usually as needed without configuration.
+ *
+ * QCBOR encodes with preferred serialization by default
+ * but provides some explicit functions that don't. This
+ * can configure QCBOR to error if they are used. This can
+ * also be used to encode dCBOR.
+ *
+ * See @ref QCBOR_ENCODE_CONFIG_PREFERRED, @ref
+ * QCBOR_ENCODE_CONFIG_DCBOR, @ref QCBOR_ENCODE_CONFIG_SORT
+ * and such.
+ *
+ * Also see QCBOREncode_ConfigReduced() if you are concerned
+ * about the amount of linked.
  */
 static void
-QCBOREncode_SerializationAny(QCBOREncodeContext *pCtx);
+QCBOREncode_Config(QCBOREncodeContext *pCtx, uint16_t uConfig);
 
 
 /**
- * @brief Select preferred serialization mode.
+ * @brief Configure the encoder, reduced object code.
  *
  * @param[in] pCtx   The encoding context for mode set.
+ * @param[in] uConfig  Bit flags for configuration options.
  *
- * Setting this mode will cause QCBOR to return an error if an attempt
- * is made to use one of the methods that produce non-preferred
- * serialization. It doesn't change anything else as QCBOR produces
- * preferred serialization by default.
+ * This is the same as QCBOREncode_Config() except it can't
+ * configure anything to do with map sorting. That includes
+ * both @ref CDE and @ref dCBOR.
  *
- * The non-preferred methods are: QCBOREncode_AddFloatNoPreferred(),
- * QCBOREncode_AddDoubleNoPreferred(),
- * QCBOREncode_OpenArrayIndefiniteLength(),
- * QCBOREncode_CloseArrayIndefiniteLength(),
- * QCBOREncode_OpenMapIndefiniteLength(),
- * QCBOREncode_CloseMapIndefiniteLength(), plus those derived from the
- * above listed.
  *
- * This mode is just a user guard to prevent accidentally calling
- * something that produces non-preferred serialization. It doesn't do
- * anything but causes errors to occur on attempts to call the above
- * listed functions. This does nothing if the library is compiled
- * QCBOR_DISABLE_ENCODE_USAGE_GUARDS.
- *
- * See @ref Serialization. It is usually not necessary to set this
- * mode, but there is usually no disadvantage to setting it.  Preferred
- * Serialization is defined in RFC 8949, section 4.1.
  */
 static void
-QCBOREncode_SerializationPreferred(QCBOREncodeContext *pCtx);
-
-
-/**
- * @brief Select CBOR deterministic encoding mode.
- *
- * @param[in] pCtx   The encoding context for mode set.
-
- * This causes QCBOR to produce CBOR Deterministic Encoding (CDE).
- * With CDE, two distant unrelated CBOR encoders will produce exactly
- * the same encoded CBOR for a given input.
- *
- * In addition to doing everything
- * QCBOREncode_SerializationPreferred() does (including exclusion of
- * indefinite lengths), this causes maps to be sorted. The map is
- * sorted automatically when QCBOREncode_CloseMap() is called.
- * QCBOREncode_CloseMap() becomes equivalent to
- * QCBOREncode_CloseAndSortMap().
- *
- * Note that linking this function causese about 30% more code from
- * the QCBOR library to be linked. Also, QCBOREncode_CloseMap() runs
- * slower, but this is probably only of consequence in very
- * constrained environments.
- *
- * See @ref Serialization. It is usually not necessary to set this
- * mode as determinism is very rarely needed. However it will
- * usually work with most protocols. CDE is defined in
- * draft-ietf-cbor-cde.
- */
-static void
-QCBOREncode_SerializationCDE(QCBOREncodeContext *pCtx);
-
-
-/**
- * @brief Select "dCBOR" encoding mode.
- *
- * @param[in] pCtx   The encoding context for mode set.
- *
- * This is a superset of CDE. This function does everything
- * QCBOREncode_SerializationCDE() does. Also it is a super set of
- * preferred serialization and does everything
- * QCBOREncode_SerializationPreferred() does.
- *
- * The main feature of dCBOR is that there is only one way to serialize a
- * particular numeric value. This changes the behavior of functions
- * that add floating-point numbers.  If the floating-point number is
- * whole, it will be encoded as an integer, not a floating-point number.
- * 0.000 will be encoded as 0x00. Precision is never lost in this
- * conversion.
- *
- * dCBOR also disallows NaN payloads. QCBOR will allow NaN payloads if
- * you pass a NaN to one of the floating-point encoding functions.
- * This mode forces all NaNs to the half-precision queit NaN. Also see
- * QCBOREncode_Allow().
- *
- * dCBOR disallows use of any simple type other than true, false and
- * NULL. In particular it disallows use of "undef" produced by
- * QCBOREncode_AddUndef().
- *
- * See @ref Serialization. Set this mode only if the protocol you are
- * implementing requires dCBOR. This mode is usually not compatible
- * with protocols that don't use dCBOR. dCBOR is defined in
- * draft-mcnally-deterministic-cbor.
- */
-static void
-QCBOREncode_SerializationdCBOR(QCBOREncodeContext *pCtx);
+QCBOREncode_ConfigReduced(QCBOREncodeContext *pCtx, uint16_t uConfig);
 
 
 
 
-/** Bit flag to be passed to QCBOREncode_Allow() to allow NaN payloads
- *  to be output by QCBOREncode_AddDouble(),
- *  QCBOREncode_AddDoubleNoPreferred(), QCBORENcode_AddFloat() and
- *  QCBOREncode_AddSingleleNoPreferred. */
-#define QCBOR_ENCODE_ALLOW_NAN_PAYLOAD 0x01
-
-/** Bit flag to be passed to QCBOREncode_Allow() output of less
- *  interoperable values. See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD */
-#define QCBOR_ENCODE_ALLOW_ALL         0xFF
-
-
-/**
- * @brief Allow encoding of less-interoperable values.
- *
- * @param[in] pCtx    The encoding context.
- * @param[in] uAllow  Bit flags indicating what to allow.
- *
- * There are a few things in the CBOR standard that are often not
- * supported and are thus not very interoperable.  By default QCBOR
- * will error if you attempt to output them. This disables that
- * error.
- *
- * See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD and 
- * @ref QCBOR_ENCODE_ALLOW_65_BIG_NEG.
- *
- * This does nothing if the library is compiled
- * QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-static void
-QCBOREncode_Allow(QCBOREncodeContext *pCtx, uint8_t uAllow);
-
 
 /**
  * @brief  Add a signed 64-bit integer to the encoded output.
@@ -3231,49 +3263,33 @@
 QCBOREncode_AddSZString(QCBOREncodeContext *pMe, const char *szString);
 
 
+
+void
+QCBOREncode_Private_CloseMapUnsorted(QCBOREncodeContext *pMe);
+
 static inline void
-QCBOREncode_SerializationCDE(QCBOREncodeContext *pMe)
+QCBOREncode_Config(QCBOREncodeContext *pMe, uint16_t uConfig)
 {
-   /* The use of a function pointer here is a little trick to reduce
-    * code linked for the common use cases that don't sort.  If this
-    * function is never linked, then QCBOREncode_CloseAndSortMap() is
-    * never linked and the amount of code pulled in is small. If the
-    * mode switch between sorting and not sorting were an if
-    * statement, then QCBOREncode_CloseAndSortMap() would always be
-    * linked even when not used. */
-   pMe->pfnCloseMap = QCBOREncode_CloseAndSortMap;
-   pMe->uMode = QCBOR_ENCODE_MODE_CDE;
+   if(uConfig & QCBOR_ENCODE_CONFIG_SORT) {
+      pMe->pfnCloseMap = QCBOREncode_CloseAndSortMap;
+   } else {
+      pMe->pfnCloseMap = QCBOREncode_Private_CloseMapUnsorted;
+   }
+   pMe->uConfigFlags = uConfig;
 }
 
 static inline void
-QCBOREncode_SerializationdCBOR(QCBOREncodeContext *pMe)
+QCBOREncode_ConfigReduced(QCBOREncodeContext *pMe, uint16_t uConfig)
 {
-   pMe->pfnCloseMap = QCBOREncode_CloseAndSortMap;
-   pMe->uMode = QCBOR_ENCODE_MODE_DCBOR;
+   if(uConfig & QCBOR_ENCODE_CONFIG_SORT) {
+      pMe->uError = 99;
+   } else {
+      pMe->uConfigFlags = uConfig;
+   }
 }
 
-static inline void
-QCBOREncode_SerializationPreferred(QCBOREncodeContext *pMe)
-{
-   pMe->uMode = QCBOR_ENCODE_MODE_PREFERRED;
-}
 
-static inline void
-QCBOREncode_SerializationAny(QCBOREncodeContext *pMe)
-{
-   pMe->uMode = QCBOR_ENCODE_MODE_ANY;
-}
 
-static inline void
-QCBOREncode_Allow(QCBOREncodeContext *pMe, const uint8_t uAllow)
-{
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   pMe->uAllow = uAllow;
-#else
-   (void)uAllow;
-   (void)pMe;
-#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-}
 
 
 
@@ -3436,7 +3452,7 @@
 #ifndef USEFULBUF_DISABLE_ALL_FLOAT
 
 static inline void
-QCBOREncode_AddDoubleNoPreferred(QCBOREncodeContext *pMe, const double dNum)
+QCBOREncode_Private_AddDoubleRaw(QCBOREncodeContext *pMe, const double dNum)
 {
    QCBOREncode_Private_AddType7(pMe,
                                 sizeof(uint64_t),
@@ -3444,21 +3460,25 @@
 }
 
 static inline void
-QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *pMe, const float fNum)
+QCBOREncode_AddDoubleNoPreferred(QCBOREncodeContext *pMe, const double dNum)
 {
-   QCBOREncode_Private_AddType7(pMe,
-                                sizeof(uint32_t),
-                                UsefulBufUtil_CopyFloatToUint32(fNum));
-}
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_DISALLOW_NON_PREFERRED_NUMBERS) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
 
+   QCBOREncode_Private_AddDoubleRaw(pMe, dNum);
+}
 
 static inline void
 QCBOREncode_AddDouble(QCBOREncodeContext *pMe, const double dNum)
 {
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
    QCBOREncode_Private_AddPreferredDouble(pMe, dNum);
-#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
-   QCBOREncode_AddDoubleNoPreferred(pMe, dNum);
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+   QCBOREncode_Private_AddDoubleRaw(pMe, dNum);
 #endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
 }
 
@@ -3488,12 +3508,33 @@
 
 
 static inline void
+QCBOREncode_Private_AddFloatRaw(QCBOREncodeContext *pMe, const float fNum)
+{
+   QCBOREncode_Private_AddType7(pMe,
+                                sizeof(uint32_t),
+                                UsefulBufUtil_CopyFloatToUint32(fNum));
+}
+
+static inline void
+QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *pMe, const float fNum)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_DISALLOW_NON_PREFERRED_NUMBERS) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+   QCBOREncode_Private_AddFloatRaw(pMe, fNum);
+}
+
+static inline void
 QCBOREncode_AddFloat(QCBOREncodeContext *pMe, const float fNum)
 {
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
    QCBOREncode_Private_AddPreferredFloat(pMe, fNum);
-#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
-   QCBOREncode_AddFloatNoPreferred(pMe, fNum);
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+   QCBOREncode_Private_AddFloatRaw(pMe, fNum);
 #endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
 }
 
@@ -3859,6 +3900,13 @@
                              bool                bNegative,
                              const UsefulBufC    BigNumber)
 {
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ONLY_PREFERRED_BIG_NUMBERS) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
    QCBOREncode_Private_BigNumberTag(pMe, uTagRequirement, bNegative);
    QCBOREncode_AddBytes(pMe, BigNumber);
 }
@@ -4533,7 +4581,7 @@
 QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint8_t uNum)
 {
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(pMe->uMode >= QCBOR_ENCODE_MODE_DCBOR) {
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ONLY_DCBOR_SIMPLE) {
       if(uNum < CBOR_SIMPLEV_FALSE || uNum > CBOR_SIMPLEV_NULL) {
          pMe->uError = QCBOR_ERR_NOT_PREFERRED;
          return;
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 804f002..e69f97f 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -223,12 +223,7 @@
 typedef struct _QCBOREncodeContext QCBORPrivateEncodeContext;
 
 
-/* These are in order of increasing strictness. Order is relied upon in
- * implementation. */
-#define QCBOR_ENCODE_MODE_ANY       0
-#define QCBOR_ENCODE_MODE_PREFERRED 1
-#define QCBOR_ENCODE_MODE_CDE       2
-#define QCBOR_ENCODE_MODE_DCBOR     3
+
 
 
 struct _QCBOREncodeContext {
@@ -236,10 +231,10 @@
    UsefulOutBuf      OutBuf;  /* Pointer to output buffer, its length and
                                * position in it. */
    uint8_t           uError;  /* Error state, always from QCBORError enum */
-   uint8_t           uMode;   /* @ref QCBOR_ENCODE_MODE_PREFERRED or related */
-   uint8_t           uAllow;  /* @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD, ... */
+   uint16_t          uConfigFlags;   /*  */
+
    void            (*pfnCloseMap)(QCBORPrivateEncodeContext *); /* Use of function
-                               * pointer explained in QCBOREncode_SerializationCDE() */
+                               * pointer explained in TODO: */
    QCBORTrackNesting nesting; /* Keep track of array and map nesting */
 };
 
@@ -436,6 +431,7 @@
 #define ABSOLUTE_VALUE(x) ((x) < 0 ? -(x) : (x))
 
 
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 84bf70f..fd16322 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -1435,7 +1435,7 @@
       case CBOR_SIMPLEV_UNDEF: /* 23 */
       case CBOR_SIMPLE_BREAK:  /* 31 */
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
-         if(uDecodeMode3Bit >= QCBOR_ENCODE_MODE_DCBOR &&
+         if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_DCBOR &&
             nAdditionalInfo == CBOR_SIMPLEV_UNDEF) {
             uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
             goto Done;
@@ -1455,7 +1455,7 @@
 
       default: /* 0-19 */
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
-         if(uDecodeMode3Bit >= QCBOR_ENCODE_MODE_DCBOR &&
+         if(uDecodeMode3Bit >= QCBOR_DECODE_MODE_DCBOR &&
             (uArgument < CBOR_SIMPLEV_FALSE || uArgument > CBOR_SIMPLEV_NULL)) {
             uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
             goto Done;
@@ -2663,7 +2663,7 @@
 
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
    if(uErr == QCBOR_SUCCESS &&
-      (pMe->uDecodeMode & QCBOR_DECODE_MODE_MASK) >= QCBOR_ENCODE_MODE_CDE &&
+      (pMe->uDecodeMode & QCBOR_DECODE_MODE_MASK) >= QCBOR_DECODE_MODE_CDE &&
       pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
       /* Traverse map checking sort order and for duplicates */
       uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem);
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index a8f01fe..a6d4d22 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -253,7 +253,7 @@
 
 
 /* Forward declaration for reference in QCBOREncode_Init() */
-static void
+void
 QCBOREncode_Private_CloseMapUnsorted(QCBOREncodeContext *pMe);
 
 
@@ -554,6 +554,14 @@
    /* A stack buffer large enough for a CBOR head */
    UsefulBuf_MAKE_STACK_UB  (pBufferForEncodedHead, QCBOR_HEAD_BUFFER_SIZE);
 
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_DISALLOW_INDEFINITE_LENGTHS &&
+      uMajorType & QCBOR_INDEFINITE_LEN_TYPE_MODIFIER) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
    UsefulBufC EncodedHead = QCBOREncode_EncodeHead(pBufferForEncodedHead,
                                                     uMajorType,
                                                     uMinLen,
@@ -659,13 +667,13 @@
    uint64_t             uNegValue;
 
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+   if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD)) {
       pMe->uError = QCBOR_ERR_NOT_ALLOWED;
       return;
    }
 #endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
 
-   if(pMe->uMode == QCBOR_ENCODE_MODE_DCBOR) {
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION) {
       IntResult = IEEE754_DoubleToInt(dNum);
       switch(IntResult.type) {
          case IEEE754_ToInt_IS_INT:
@@ -719,13 +727,13 @@
    uint64_t             uNegValue;
 
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(IEEE754_SingleHasNaNPayload(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+   if(IEEE754_SingleHasNaNPayload(fNum) && !(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD)) {
       pMe->uError = QCBOR_ERR_NOT_ALLOWED;
       return;
    }
 #endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
 
-   if(pMe->uMode == QCBOR_ENCODE_MODE_DCBOR) {
+   if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION) {
       IntResult = IEEE754_SingleToInt(fNum);
       switch(IntResult.type) {
          case IEEE754_ToInt_IS_INT:
@@ -952,6 +960,13 @@
    uint8_t    uMajorType;
    UsefulBufC BigNumberNLZ;
 
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(!bPreferred && pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ONLY_PREFERRED_BIG_NUMBERS) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
    BigNumberNLZ = QCBOREncode_Private_SkipLeadingZeros(BigNumber);
 
    static const uint8_t twoExp64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@@ -1153,14 +1168,11 @@
 QCBOREncode_Private_OpenMapOrArrayIndefiniteLength(QCBOREncodeContext *pMe,
                                                    const uint8_t       uMajorType)
 {
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(pMe->uMode >= QCBOR_ENCODE_MODE_PREFERRED) {
-      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
-      return;
-   }
-#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
    /* Insert the indefinite length marker (0x9f for arrays, 0xbf for maps) */
    QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, 0, 0);
+   if(pMe->uError) {
+      return;
+   }
 
    /* Call the definite-length opener just to do the bookkeeping for
     * nesting.  It will record the position of the opening item in the
@@ -1287,7 +1299,7 @@
  * See QCBOREncode_SerializationCDE() implemention for explantion for why
  * this exists in this form.
  */
-static void
+void
 QCBOREncode_Private_CloseMapUnsorted(QCBOREncodeContext *pMe)
 {
    QCBOREncode_Private_CloseMapOrArray(pMe, CBOR_MAJOR_TYPE_MAP);
diff --git a/test/float_tests.c b/test/float_tests.c
index 892942d..98ebc3d 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -414,7 +414,7 @@
    for(uTestIndex = 0; FloatTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
       pTestCase = &FloatTestCases[uTestIndex];
 
-      if(uTestIndex == 40) {
+      if(uTestIndex == 2) {
          uDecoded = 1;
       }
 
@@ -444,7 +444,7 @@
 
       /* Number Encode of CDE */
       QCBOREncode_Init(&EnCtx, TestOutBuffer);
-      QCBOREncode_SerializationCDE(&EnCtx);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_CDE);
       QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
       uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
 
@@ -457,7 +457,7 @@
 
       /* Number Encode of dCBOR */
       QCBOREncode_Init(&EnCtx, TestOutBuffer);
-      QCBOREncode_SerializationdCBOR(&EnCtx);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR);
       QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
       uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
 
@@ -470,7 +470,7 @@
 
       if(pTestCase->fNumber != 0) {
          QCBOREncode_Init(&EnCtx, TestOutBuffer);
-         QCBOREncode_SerializationdCBOR(&EnCtx);
+         QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR);
          QCBOREncode_AddFloat(&EnCtx, pTestCase->fNumber);
          uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
 
@@ -567,7 +567,7 @@
 
       /* NaN Encode of Preferred */
       QCBOREncode_Init(&EnCtx, TestOutBuffer);
-      QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
       QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
       uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
       if(uErr != QCBOR_SUCCESS) {
@@ -616,7 +616,7 @@
 
       /* NaN Encode of Not Preferred */
       QCBOREncode_Init(&EnCtx, TestOutBuffer);
-      QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
       QCBOREncode_AddDoubleNoPreferred(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
       uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
       if(uErr != QCBOR_SUCCESS) {
@@ -628,7 +628,7 @@
 
       /* NaN Decode of Not Preferred */
       QCBORDecode_Init(&DCtx, pNaNTestCase->Preferred, 0);
-      QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
       uErr = QCBORDecode_GetNext(&DCtx, &Item);
       if(uErr != QCBOR_SUCCESS) {
          return MakeTestResultCode(uTestIndex+100, 12, uErr);
@@ -664,7 +664,7 @@
 
       /* NaN Decode of Not Preferred */
       QCBORDecode_Init(&DCtx, pNaNTestCase->NotPreferred, 0);
-      QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
       uErr = QCBORDecode_GetNext(&DCtx, &Item);
       if(uErr != QCBOR_SUCCESS) {
          return MakeTestResultCode(uTestIndex+100, 13, uErr);
@@ -677,8 +677,7 @@
 
       /* NaN Encode of DCBOR */
       QCBOREncode_Init(&EnCtx, TestOutBuffer);
-      QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
-      QCBOREncode_SerializationdCBOR(&EnCtx);
+      QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR | QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
       QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
       uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
       if(uErr != QCBOR_SUCCESS) {
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 9360da2..0fabc4f 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2610,7 +2610,7 @@
       }
 #endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
 
-      if(nIndex == 0) {
+      if(nIndex == 57) {
          uCBORError = 9; /* For setting break points */
       }
 
@@ -10612,12 +10612,17 @@
       {"\xa1\x81\x1c\x01", 4},
       QCBOR_ERR_MAP_LABEL_TYPE
    },
-
+   { "map with map label ",
+      QCBOR_DECODE_MODE_CDE,
+      {"\xa1\xa1\x00\x01\x02", 5},
+      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
-   }};
+      QCBOR_ERR_PREFERRED_CONFORMANCE
+   }
+};
 
 
 static UsefulBufC CorrectlySorted[] = {
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 3c758f2..b756053 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -722,7 +722,7 @@
    nReturn = 0;
 
    QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_Allow(&ECtx, QCBOR_ENCODE_ALLOW_ALL);
+   QCBOREncode_Config(&ECtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
 
    AddAll(&ECtx);
 
@@ -739,7 +739,7 @@
 
    /* Also test size calculation */
    QCBOREncode_Init(&ECtx, SizeCalculateUsefulBuf);
-   QCBOREncode_Allow(&ECtx, QCBOR_ENCODE_ALLOW_ALL);
+   QCBOREncode_Config(&ECtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
 
    AddAll(&ECtx);
 
@@ -3122,10 +3122,10 @@
 
    switch(pTest->eSerialization) {
       case EAM_Pref:
-         QCBOREncode_SerializationPreferred(pEnc);
+         QCBOREncode_Config(pEnc, QCBOR_ENCODE_CONFIG_PREFERRED );
          break;
       case EAM_CDE:
-         QCBOREncode_SerializationCDE(pEnc);
+         QCBOREncode_Config(pEnc, QCBOR_ENCODE_CONFIG_CDE);
          break;
 
       default:
@@ -3302,7 +3302,6 @@
       } else {
          EAMTestSetup(pTest, &EC);
 
-         //QCBOREncode_AddDecimalFractionBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
          QCBOREncode_AddTDecimalFractionBigMantissa(&EC, QCBOR_ENCODE_AS_TAG, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
          uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
          if(uErr) {
@@ -3969,7 +3968,8 @@
 
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
 
-   QCBOREncode_SerializationCDE(&EC);
+   QCBOREncode_Config(&EC, QCBOR_ENCODE_CONFIG_CDE);
+
 
    /* Items added to test sorting and preferred encoding of numbers and floats */
    QCBOREncode_OpenMap(&EC);
@@ -4008,7 +4008,7 @@
 #ifndef  QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
    /* Next, make sure methods that encode non-CDE error out */
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_SerializationCDE(&EC);
+   QCBOREncode_Config(&EC, QCBOR_ENCODE_CONFIG_CDE);
    QCBOREncode_OpenMapIndefiniteLength(&EC);
    QCBOREncode_CloseMap(&EC);
    if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
@@ -4027,8 +4027,8 @@
    QCBORError         uExpectedErr;
 
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+   QCBOREncode_Config(&EC, QCBOR_ENCODE_CONFIG_DCBOR);
 
-   QCBOREncode_SerializationdCBOR(&EC);
 
    /* Items added to test sorting and preferred encoding of numbers and floats */
    QCBOREncode_OpenMap(&EC);
@@ -4065,7 +4065,7 @@
 #ifndef  QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
    /* Indefinite-length map */
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_SerializationdCBOR(&EC);
+   QCBOREncode_Config(&EC, QCBOR_ENCODE_CONFIG_DCBOR);
    QCBOREncode_OpenMapIndefiniteLength(&EC);
    QCBOREncode_CloseMap(&EC);
    if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
@@ -4074,7 +4074,7 @@
 
    /* Indefinite-length array */
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_SerializationdCBOR(&EC);
+   QCBOREncode_Config(&EC, QCBOR_ENCODE_CONFIG_DCBOR);
    QCBOREncode_OpenArrayIndefiniteLength(&EC);
    QCBOREncode_CloseMap(&EC);
    if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
@@ -4084,7 +4084,7 @@
 
    /* The "undef" special value */
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_SerializationdCBOR(&EC);
+   QCBOREncode_Config(&EC, QCBOR_ENCODE_CONFIG_DCBOR);
    QCBOREncode_AddUndef(&EC);
    QCBOREncode_CloseMap(&EC);
    if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {