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) {