Full big num implementation with preferred serialization and 65-bit negs (#219)
Encoding and decoding of negative big numbers now takes into account the offset of 1 for all CBOR negative numbers. Also, big numbers are encoded with preferred serialization -- when they can be encoded with type 0 and type 1 integers, they are. Encoding and decoding big numbers is no longer a pass through for tagging a binary string.
This is an incompatible change with QCBOR 1.x. A mode configuration call is added to return to the 1.x behavior if desired.
This is because of the realization in work on 65-bit negative numbers that big numbers need preferred serialization.
This affects big floats and decimal fractions when the mantissa is a big number.
New methods to encode big numbers with non-preferred serialization are added.
A new method is added to process a raw big number for the offset of one for negatives. This is outside of spiffy decode. It does a little big number arithmetic.
dCBOR numeric reduction now includes 65-bit integers both for number encoding and decoding.
* Checkpoint work on 65-bit negs
* check point ... mostly working
* Checkpoint work on preferred bignums
* Tests passing...
* bignum encode improved to near completion
* Fix #ifdef fan out and error condition bug
* Code tidiness
* debugging unreplicatable CI failure
* v1 compat mode; EAM encode test fan out starting
* debugging ci...
* hacking ci failures
* CI failure hacking
* CI hacking
* Clean up some left over junk
* more leftover junk
* Clean up test leftovers
* Fixes for #ifdef fan out
* Fix conversion of uint to zero, even though it's never used
* REmove more junk
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 1441ade..e51ebc6 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -31,7 +31,6 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
-
#ifndef qcbor_common_h
#define qcbor_common_h
@@ -556,6 +555,9 @@
/** 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,
+
/** 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 c2da5a2..f2f4a4b 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -247,7 +247,7 @@
/** Type for an integer that decoded either between @c INT64_MIN and
* @c INT32_MIN or @c INT32_MAX and @c INT64_MAX. Data is in member
- * @c val.int64. */
+ * @c val.int64. See also \ref QCBOR_TYPE_65BIT_NEG_INT */
#define QCBOR_TYPE_INT64 2
/** Type for an integer that decoded to a more than @c INT64_MAX and
@@ -269,11 +269,19 @@
#define QCBOR_TYPE_TEXT_STRING 7
/** Type for a positive big number. Data is in @c val.bignum, a
- * pointer and a length. */
+ * pointer and a length. See QCBORDecode_BignumPreferred(). */
#define QCBOR_TYPE_POSBIGNUM 9
/** Type for a negative big number. Data is in @c val.bignum, a
- * pointer and a length. */
+ * pointer and a length. Type 1 integers in the range of [-2^64,
+ * -2^63 - 1] are returned in this type. 1 MUST be subtracted from
+ * what is returned to get the actual value. This is because of the
+ * way CBOR negative numbers are represented. QCBOR doesn't do this
+ * because it can't be done without storage allocation and QCBOR
+ * avoids storage allocation for the most part. For example, if 1 is
+ * subtraced from a negative big number that is the two bytes 0xff
+ * 0xff, the result would be 0x01 0x00 0x00, one byte longer than
+ * what was received. See QCBORDecode_BignumPreferred(). */
#define QCBOR_TYPE_NEGBIGNUM 10
/** Type for [RFC 3339] (https://tools.ietf.org/html/rfc3339) date
@@ -504,6 +512,7 @@
/** The value for @c uDataType @ref QCBOR_TYPE_POSBIGNUM and
* @ref QCBOR_TYPE_NEGBIGNUM. */
UsefulBufC bigNum;
+
/** See @ref QCBOR_TYPE_UKNOWN_SIMPLE */
uint8_t uSimple;
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
@@ -1373,6 +1382,68 @@
QCBORDecode_SetError(QCBORDecodeContext *pCtx, QCBORError uError);
+/**
+ * @brief Decode a preferred serialization big number.
+ *
+ * @param[in] Item The number to process.
+ * @param[in] BigNumBuf The buffer to output to.
+ * @param[out] pBigNum The resulting big number.
+ * @param[out] pIsNegative The sign of the resulting big number.
+ *
+ * This exists to process an item that is expected to be a big number
+ * encoded with preferred serialization. This processing is not part
+ * of the main decoding because of the number of CBOR types it
+ * involves, because it needs a buffer to output to, and to keep code
+ * size of the core decoding small.
+ *
+ * This can also be used to do the subtraction of 1 for negative big
+ * numbers even if preferred serialization of big numbers is not in
+ * use.
+ *
+ * This works on all CBOR type 0 and 1 integers and all tag 2 and 3
+ * big numbers. In terms of QCBOR types, this works on
+ * \ref QCBOR_TYPE_INT64, \ref QCBOR_TYPE_UINT64,
+ * \ref QCBOR_TYPE_65BIT_NEG, \ref QCBOR_TYPE_POSBIGNUM and
+ * \ref QCBOR_TYPE_NEGBIGNUM.
+ *
+ * This always returns the result as a big number. The integer types 0
+ * and 1 are converted. Leading zeros are removed. The value 0 is
+ * always returned as a one-byte big number with the value 0x00.
+
+ * If \c BigNumBuf is too small, \c pBigNum.ptr will be \c NULL and \c
+ * pBigNum.len reports the required length. The size of \c BigNumBuf
+ * might have to be one larger than the size of the tag 2 or 3 being
+ * decode because of two cases. In CBOR the value of a tag 3 big
+ * number is -n - 1. The subtraction of one might have a carry. For
+ * example, an encoded tag 3 that is 0xff, is returned here as 0x01
+ * 0x00. The other case is a empty tag 2 which is returned as a
+ * one-byte big number with the value 0x00. (This is the only place
+ * in all of RFC 8949 except for indefinite length strings where the
+ * encoded buffer off the wire can't be returned directly, the only
+ * place some storage allocation is required.)
+ *
+ * This is the decode-side implementation of preferred serialization
+ * of big numbers described in section 3.4.3 of RFC 8949. It
+ * implements the decode-side unification of big numbers and regular
+ * integers.
+ *
+ * This can also be used if you happen to want type 0 and type 1
+ * integers converted to big numbers.
+ *
+ * If QCBOR is being used in an environment with a full big number
+ * library, it may be better (less object code) to use the big number
+ * library than this, particularly to subtract one for tag 3.
+ *
+ * Finally, the object code for this function is suprisingly large,
+ * almost 1KB. This is due to the number of CBOR data types, and the
+ * big number math required to subtract one and the buffer sizing
+ * issue it brings.
+ */
+QCBORError
+QCBORDecode_BignumPreferred(const QCBORItem Item,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pBigNum,
+ bool *pIsNegative);
/**
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index a9aebff..96a91e1 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -471,6 +471,36 @@
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.
+ *
+ * @param[in] pCtx The encoding context for mode set.
+ *
+ * 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.
+ */
+static void
+QCBOREncode_SerializationAny(QCBOREncodeContext *pCtx);
+
+
/**
* @brief Select preferred serialization mode.
*
@@ -555,8 +585,6 @@
* This mode forces all NaNs to the half-precision queit NaN. Also see
* QCBOREncode_Allow().
*
- * dCBOR also disallows 65-bit negative integers.
- *
* dCBOR disallows use of any simple type other than true, false and
* NULL. In particular it disallows use of "undef" produced by
* QCBOREncode_AddUndef().
@@ -578,13 +606,8 @@
* QCBOREncode_AddSingleleNoPreferred. */
#define QCBOR_ENCODE_ALLOW_NAN_PAYLOAD 0x01
-/** Bit flag to be passed to QCBOREncode_Allow() to allow use of
- * QCBOREncode_AddNegativeUInt64(). */
-#define QCBOR_ENCODE_ALLOW_65_BIG_NEG 0x02
-
/** Bit flag to be passed to QCBOREncode_Allow() output of less
- * interoperable values. See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD, and
- * @ref QCBOR_ENCODE_ALLOW_65_BIG_NEG. */
+ * interoperable values. See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD */
#define QCBOR_ENCODE_ALLOW_ALL 0xFF
@@ -608,6 +631,12 @@
QCBOREncode_Allow(QCBOREncodeContext *pCtx, uint8_t uAllow);
+/*
+ * QCBOR_ENCODE_CONFIG_V1_COMPAT
+ */
+static void
+QCBOREncode_Setv1Compatibility(QCBOREncodeContext *pCtx);
+
/**
* @brief Add a signed 64-bit integer to the encoded output.
@@ -617,14 +646,14 @@
*
* The integer will be encoded and added to the CBOR output.
*
- * This function figures out the size and the sign and encodes in the
- * correct minimal CBOR. Specifically, it will select CBOR major type
+ * This function figures out the size and the sign and encodes using
+ * CBOR preferred serialization. Specifically, it will select CBOR major type
* 0 or 1 based on sign and will encode to 1, 2, 4 or 8 bytes
* depending on the value of the integer. Values less than 24
* effectively encode to one byte because they are encoded in with the
- * CBOR major type. This is a neat and efficient characteristic of
+ * CBOR major type. This is a neat and efficient characteristic of
* CBOR that can be taken advantage of when designing CBOR-based
- * protocols. If integers like tags can be kept between -23 and 23
+ * protocols. If integers can be kept between -23 and 23
* they will be encoded in one byte including the major type.
*
* If you pass a smaller int, say an @c int16_t or a small value, say
@@ -666,7 +695,7 @@
* @param[in] pCtx The encoding context to add the integer to.
* @param[in] uNum The integer to add.
*
- * The integer will be encoded and added to the CBOR output.
+ * The integer is encoded and added to the CBOR output.
*
* The only reason so use this function is for integers larger than
* @c INT64_MAX and smaller than @c UINT64_MAX. Otherwise
@@ -696,10 +725,6 @@
* the sign as a bit) which is possible because CBOR happens to
* support such integers.
*
- * Because use of this is discouraged. It must be explicitly allowed
- * by passing @ref QCBOR_ENCODE_ALLOW_65_BIG_NEG to a call to
- * QCBOREncode_Allow().
- *
* The actual value encoded is -uNum - 1. That is, give 0 for uNum to
* transmit -1, give 1 to transmit -2 and give UINT64_MAX to transmit
* -UINT64_MAX-1 (18446744073709551616). The interface is odd like
@@ -707,9 +732,9 @@
* QCBOR (making this a complete CBOR implementation).
*
* The most negative value QCBOREncode_AddInt64() can encode is
- * -9223372036854775808 which is -2^63 or negative 0x800000000000.
+ * -9223372036854775808 which is -(2^63) or negative 0x800000000000.
* This can encode from -9223372036854775809 to -18446744073709551616
- * or -2^63 - 1 to -2^64. Note that it is not possible to represent
+ * or -(2^63 +1) to -(2^64). Note that it is not possible to represent
* positive or negative 18446744073709551616 in any standard C data
* type.
*
@@ -717,14 +742,14 @@
* @ref QCBOR_TYPE_INT64. Integers in the range of -9223372036854775809
* to -18446744073709551616 are returned as @ref QCBOR_TYPE_65BIT_NEG_INT.
*
- * WARNING: some CBOR decoders will be unable to decode -2^63 - 1 to
- * -2^64. Also, most CPUs do not have registers that can represent
+ * WARNING: some CBOR decoders will be unable to decode -(2^63 + 1) to
+ * -(2^64). Also, most CPUs do not have registers that can represent
* this range. If you need 65-bit negative integers, you likely need
* negative 66, 67 and 68-bit negative integers so it is likely better
* to use CBOR big numbers where you can have any number of bits. See
* QCBOREncode_AddTNegativeBignum() and @ref Serialization.
*/
-void
+static void
QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pCtx, uint64_t uNum);
static void
@@ -733,6 +758,7 @@
static void
QCBOREncode_AddNegativeUInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, uint64_t uNum);
+
/**
* @brief Add a UTF-8 text string to the encoded output.
*
@@ -806,39 +832,42 @@
* @param[in] pCtx The encoding context to add the double to.
* @param[in] dNum The double-precision number to add.
*
- * This encodes and outputs a floating-point number. CBOR major type 7
- * is used.
- *
- * This implements preferred serialization, selectively encoding the
- * double-precision floating-point number as either double-precision,
+ * This encodes using preferred serialization, selectively encoding
+ * the input floating-point number as either double-precision,
* single-precision or half-precision. Infinity, NaN and 0 are always
- * encoded as half-precision. If no precision will be lost in the
- * conversion to half-precision, then it will be converted and
- * encoded. If not and no precision will be lost in conversion to
- * single-precision, then it will be converted and encoded. If not,
- * then no conversion is performed, and it encoded as a
- * double-precision.
+ * encoded as half-precision. The reduction to single-precision or
+ * half-precision is only performed if there is no loss or precision.
*
* Half-precision floating-point numbers take up 2 bytes, half that of
- * single-precision, one quarter of double-precision
+ * single-precision, one quarter of double-precision. This can reduce
+ * the size of encoded output a lot, especially if the values 0,
+ * infinity and NaN occur frequently.
*
- * This automatically reduces the size of encoded CBOR, maybe even by
- * four if most of values are 0, infinity or NaN.
+ * QCBOR decoding returns double-precision reversing this reduction.
*
- * When decoded, QCBOR will usually return these values as
- * double-precision.
- *
- * It is possible to disable this preferred serialization when compiling
- * QCBOR. In that case, this functions the same as
- * QCBOREncode_AddDoubleNoPreferred().
+ * Normally this outputs only CBOR major type 7. If
+ * QCBOREncode_SerializationdCBOR() is called to enter dCBOR mode,
+ * floating-point inputs that are whole integers are further reduced
+ * to CBOR type 0 and 1. This is a unification of the floating-point
+ * and integer number spaces such that there is only one encoding of
+ * any numeric value. Note that this will result in the whole integers
+ * from -(2^63+1) to -(2^64) being encode as CBOR major type 1 which
+ * can't be directly decoded into an int64_t or uint64_t. See
+ * QCBORDecode_GetNumberConvertPrecisely(), a good method to use to
+ * decode dCBOR.
*
* Error handling is the same as QCBOREncode_AddInt64().
*
+ * It is possible that preferred serialization is disabled when the
+ * QCBOR library was built. In that case, this functions the same as
+ * QCBOREncode_AddDoubleNoPreferred().
+ *
* See also QCBOREncode_AddDoubleNoPreferred(), QCBOREncode_AddFloat()
* and QCBOREncode_AddFloatNoPreferred() and @ref Floating-Point.
*
* By default, this will error out on an attempt to encode a NaN with
- * a payload. See QCBOREncode_Allow() and @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD.
+ * a payload. See QCBOREncode_Allow() and @ref
+ * QCBOR_ENCODE_ALLOW_NAN_PAYLOAD.
* If preferred serialization is disabled at compliation, this check for
* for NaN payloads is disabled.
*/
@@ -859,7 +888,7 @@
* @param[in] fNum The single-precision number to add.
*
* This is identical to QCBOREncode_AddDouble() except the input is
- * single-precision.
+ * single-precision. It also supports dCBOR.
*
* See also QCBOREncode_AddDouble(), QCBOREncode_AddDoubleNoPreferred(),
* and QCBOREncode_AddFloatNoPreferred() and @ref Floating-Point.
@@ -1185,80 +1214,141 @@
/**
- * @brief Add a positive big number to the encoded output.
+ * @brief Add a positive big number using preferred serialization.
*
* @param[in] pCtx The encoding context to add the big number to.
* @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
* @ref QCBOR_ENCODE_AS_BORROWED.
- * @param[in] Bytes Pointer and length of the big number.
+ * @param[in] BigNumber Pointer and length of the big number.
*
- * Big numbers are integers larger than 64-bits. Their format is
- * described in [RFC 8949] (https://tools.ietf.org/html/rfc8949).
+ * @c BigNumber makes up an aribtrary precision integer in
+ * network/big-endian byte order. The first byte is the most
+ * significant.
*
- * It is output as CBOR major type 2, a binary string, with tag
- * @ref CBOR_TAG_POS_BIGNUM indicating the binary string is a positive
- * big number.
+ * If the value in @c BigNumber is greater than @c UINT64_MAX, this
+ * will be encoded as the CBOR tag @ref CBOR_TAG_POS_BIGNUM. If less
+ * then, it will be output as a type 0 integer. This is as required
+ * for preferred serialization described in RFC 8949 section 3.4.3.
*
- * Often big numbers are used to represent cryptographic keys,
- * however, COSE which defines representations for keys chose not to
- * use this particular type.
+ * See also QCBOREncode_AddTPositiveBignumNoPreferred().
+ *
+ * In QCBOR v2, this is NOT backwards compatible with v1 by default. It
+ * can be made to be compatible with QCBOREncode_Setv1Compatibility().
+ * The incompatibility is that in v1 this function was a straight through
+ * copy, where for v2 this will use preferred serialization to encode
+ * small integers as type 1.
+ *
+ * Sometimes big numbers are used to represent parts of
+ * cryptographic keys, however, COSE which defines representations for
+ * keys does, not use this particular type.
*/
-static void
+void
QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pCtx,
uint8_t uTagRequirement,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
static void
QCBOREncode_AddTPositiveBignumToMapSZ(QCBOREncodeContext *pCtx,
const char *szLabel,
uint8_t uTagRequirement,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
static void
QCBOREncode_AddTPositiveBignumToMapN(QCBOREncodeContext *pCtx,
int64_t nLabel,
uint8_t uTagRequirement,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
+/**
+ * @brief Add a positive big number using preferred serialization.
+ *
+ * @param[in] pCtx The encoding context to add the big number to.
+ * @param[in] BigNumber Pointer and length of the big number.
+ *
+ * This operates the same as QCBOREncode_AddTPositiveBignum(),
+ * but always outputs the tag number. This exists for backwards
+ * compatibility with an early version of QCBOR.
+ *
+ * In QCBOR v2, this is NOT backwards compatible with v1 by default. It
+ * can be made to be compatible with QCBOREncode_Setv1Compatibility().
+ */
static void
QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
static void
QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx,
const char *szLabel,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
static void
QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx,
int64_t nLabel,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
/**
- * @brief Add a negative big number to the encoded output.
+ * @brief Add a positive big number without preferred serialization.
*
* @param[in] pCtx The encoding context to add the big number to.
* @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
* @ref QCBOR_ENCODE_AS_BORROWED.
- * @param[in] Bytes Pointer and length of the big number.
+ * @param[in] BigNumber Pointer and length of the big number.
*
- * Big numbers are integers larger than 64-bits. Their format is
- * described in [RFC 8949] (https://tools.ietf.org/html/rfc8949).
+ * This is mostly a straight-through output of the bytes that make up the big
+ * number. There is no conversion to CBOR type 0 integers for values
+ * smaller than @c UINT64_MAX.
*
- * It is output as CBOR major type 2, a binary string, with tag
- * @ref CBOR_TAG_NEG_BIGNUM indicating the binary string is a negative
- * big number.
- *
- * Often big numbers are used to represent cryptographic keys,
- * however, COSE which defines representations for keys chose not to
- * use this particular type.
+ * See also QCBOREncode_AddTPositiveBignum().
*/
-static void
+void
+QCBOREncode_AddTPositiveBignumNoPreferred(QCBOREncodeContext *pCtx,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNumber);
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMap(QCBOREncodeContext *pCtx,
+ const char *szLabel,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNumber);
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMapN(QCBOREncodeContext *pCtx,
+ int64_t nLabel,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNumber);
+
+/**
+ * @brief Add a negative big number using preferred serialization.
+ *
+ * @param[in] pCtx The encoding context to add the big number to.
+ * @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
+ * @ref QCBOR_ENCODE_AS_BORROWED.
+ * @param[in] BigNum Pointer and length of the big number.
+ *
+ * This outputs @c BigNum as a type 1 if the value is greater than or
+ * equal to -(2^64), and as tag 3 if less than. This is as required
+ * for preferred serialization described in RFC 8949 3.4.3.
+ *
+ * Note that the actual encoded value is 1 more than the value of
+ * BigNum as required for all encoded negative numbers in CBOR. The
+ * implementation of this function does some big number arithmatic to
+ * effect this.
+ *
+ * See also QCBOREncode_AddTNegativeBignumNoPreferred().
+ *
+ * B y default, this function is not comparible with QCBOR v1. In
+ * QCBOR v1 this just copied @c BigNum straight through without
+ * adjusting the negative number by 1 and without preferred
+ * serialization. By calling QCBOREncode_Setv1Compatibility() , this
+ * will go back to the v1 behavior. This behavior in v2 is more
+ * correct.
+ */
+void
QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pCtx,
uint8_t uTagRequirement,
- UsefulBufC Bytes);
+ UsefulBufC BigNum);
static void
QCBOREncode_AddTNegativeBignumToMapSZ(QCBOREncodeContext *pCtx,
@@ -1273,19 +1363,68 @@
UsefulBufC Bytes);
+/**
+ * @brief Add a negative big number using preferred serialization.
+ *
+ * @param[in] pCtx The encoding context to add the big number to.
+ * @param[in] BigNumber Pointer and length of the big number.
+ *
+ * This operates the same as QCBOREncode_AddTNegativeBignum(),
+ * but always outputs the tag number. This exists for backwards
+ * compatibility with an early version of QCBOR.
+ *
+ * In QCBOR v2, this is NOT backwards compatible with v1 by default. It
+ * can be made to be compatible with QCBOREncode_Setv1Compatibility().
+ */
static void
QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
static void
QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx,
const char *szLabel,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
static void
QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx,
int64_t nLabel,
- UsefulBufC Bytes);
+ UsefulBufC BigNumber);
+
+
+/**
+ * @brief Add a negative big number without preferred serialization.
+ *
+ * @param[in] pCtx The encoding context to add the big number to.
+ * @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
+ * @ref QCBOR_ENCODE_AS_BORROWED.
+ * @param[in] BigNumber Pointer and length of the big number.
+ *
+ * This always outputs the value as tag 3 (negative big number). It doesn't
+ * output numbers greater than -UINT64_MAX as type 1 integers as would
+ * be done with preferred serialization.
+ *
+ * This is not a straight through copy of the big number. It adds one, the
+ * offset used for all negative numbers in CBOR.
+ *
+ * If QCBOREncode_Setv1Compatibility() is set, then this does a
+ * straight-through copy without adding one.
+ *
+ * See also QCBOREncode_AddTNegativeBignum().
+ */
+void
+QCBOREncode_AddTNegativeBignumNoPreferred(QCBOREncodeContext *pCtx,
+ uint8_t uTagRequirement,
+ UsefulBufC BigNumber);
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMap(QCBOREncodeContext *pCtx,
+ const char *szLabel,
+ uint8_t uTagRequirement,
+ UsefulBufC BigNumber);
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMapN(QCBOREncodeContext *pCtx,
+ int64_t nLabel,
+ uint8_t uTagRequirement,
+ UsefulBufC BigNumber);
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
@@ -2602,10 +2741,10 @@
void
QCBOREncode_Private_AddExpMantissa(QCBOREncodeContext *pCtx,
uint64_t uTag,
+ int64_t nExponent,
UsefulBufC BigNumMantissa,
bool bBigNumIsNegative,
- int64_t nMantissa,
- int64_t nExponent);
+ int64_t nMantissa);
@@ -2666,6 +2805,12 @@
}
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
@@ -2676,6 +2821,13 @@
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
}
+static inline void
+QCBOREncode_Setv1Compatibility(QCBOREncodeContext *pMe)
+{
+ pMe->uConfig = QCBOR_ENCODE_CONFIG_V1_COMPAT;
+}
+
+
static inline void
QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe,
@@ -2723,12 +2875,15 @@
static inline void
+QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pMe, const uint64_t uValue)
+{
+ QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_NEGATIVE_INT, uValue, 0);
+}
+
+static inline void
QCBOREncode_AddNegativeUInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, uint64_t uNum)
{
- /* Use _AddBuffer() because _AddSZString() is defined below, not above */
- QCBOREncode_Private_AddBuffer(pMe,
- CBOR_MAJOR_TYPE_TEXT_STRING,
- UsefulBuf_FromSZ(szLabel));
+ QCBOREncode_AddSZString(pMe, szLabel);
QCBOREncode_AddNegativeUInt64(pMe, uNum);
}
@@ -3109,16 +3264,6 @@
}
-static inline void
-QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pMe,
- const uint8_t uTagRequirement,
- const UsefulBufC Bytes)
-{
- if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
- QCBOREncode_AddTag(pMe, CBOR_TAG_POS_BIGNUM);
- }
- QCBOREncode_AddBytes(pMe, Bytes);
-}
static inline void
QCBOREncode_AddTPositiveBignumToMapSZ(QCBOREncodeContext *pMe,
@@ -3169,16 +3314,7 @@
}
-static inline void
-QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pMe,
- const uint8_t uTagRequirement,
- const UsefulBufC Bytes)
-{
- if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
- QCBOREncode_AddTag(pMe, CBOR_TAG_NEG_BIGNUM);
- }
- QCBOREncode_AddBytes(pMe, Bytes);
-}
+
static inline void
QCBOREncode_AddTNegativeBignumToMapSZ(QCBOREncodeContext *pMe,
@@ -3246,10 +3382,10 @@
}
QCBOREncode_Private_AddExpMantissa(pMe,
uTag,
+ nBase10Exponent,
NULLUsefulBufC,
false,
- nMantissa,
- nBase10Exponent);
+ nMantissa);
}
static inline void
@@ -3334,10 +3470,10 @@
}
QCBOREncode_Private_AddExpMantissa(pMe,
uTag,
+ nBase10Exponent,
Mantissa,
bIsNegative,
- 0,
- nBase10Exponent);
+ 0);
}
static inline void
@@ -3433,10 +3569,10 @@
}
QCBOREncode_Private_AddExpMantissa(pMe,
uTag,
+ nBase2Exponent,
NULLUsefulBufC,
false,
- nMantissa,
- nBase2Exponent);
+ nMantissa);
}
static inline void
@@ -3515,10 +3651,10 @@
}
QCBOREncode_Private_AddExpMantissa(pMe,
uTag,
+ nBase2Exponent,
Mantissa,
bIsNegative,
- 0,
- nBase2Exponent);
+ 0);
}
static inline void
@@ -3966,8 +4102,7 @@
{
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
if(pMe->uMode >= QCBOR_ENCODE_MODE_DCBOR) {
- if(uNum < CBOR_SIMPLEV_FALSE ||
- uNum > CBOR_SIMPLEV_NULL) {
+ 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 0a3fb9c..d7abe31 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -231,12 +231,19 @@
#define QCBOR_ENCODE_MODE_DCBOR 3
+/* Operate in compatibility with QCBOR 1.0
+ * So far the differences are:
+ * - AddNegativeBigNum and AddBigFloat with a negative big num
+ */
+#define QCBOR_ENCODE_CONFIG_V1_COMPAT 0x01
+
struct _QCBOREncodeContext {
/* PRIVATE DATA STRUCTURE */
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 uConfig; /* QCBOR_ENCODE_CONFIG_xxx */
uint8_t uAllow; /* @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD, ... */
void (*pfnCloseMap)(QCBORPrivateEncodeContext *); /* Use of function
* pointer explained in QCBOREncode_SerializationCDE() */
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index dc4baf7..26325d8 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -299,41 +299,53 @@
#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
/**
- * @brief Decode next as a number with precision-preserving conversions.
+ * @brief dCBOR Decode next as a number with precision-preserving conversions.
*
* @param[in] pCtx The decode context.
- * @param[out] pNumber The returned 64-bit signed integer.
+ * @param[out] pNumber The returned number.
*
- * This will get the next item as a number and return it as a C
- * data type such that no precision is lost.
+ * This gets the next item as a number and returns it as a C data type
+ * such that no precision is lost.
*
- * The CBOR input can be integers (major type 0 or 1) or floats (major type 7).
- * If not these \ref QCBOR_ERR_UNEXPECTED_TYPE will be set.
+ * This is primarily works with integers and floats for both the
+ * to-be-decoded CBOR and the decoded types.
+ *
+ * The CBOR input can be integers (major type 0 or 1) or floats (major
+ * type 7). If not these, \ref QCBOR_ERR_UNEXPECTED_TYPE will be set.
*
* The conversion is as follows.
*
- * Whole numbers between \c INT64_MIN and \c INT64_MAX will
- * be returned as \ref QCBOR_TYPE_INT64. This includes
- * conversion of float-point values that are whole numbers.
- *
- * Whole numbers between \c INT64_MAX and \c UINT64_MAX will
- * be returned as \ref QCBOR_TYPE_UINT64, again including
+ * Whole numbers from \c INT64_MIN to \c INT64_MAX will be returned as
+ * int64_t indicated as \ref QCBOR_TYPE_INT64. This includes
* conversion of floating-point values that are whole numbers.
*
- * The whole numbers called "65-bit negative" in CBOR (-2^63 - 1 to -2^64) are a
- * special case. Some of them can be converted to a double without
- * loss of precision and some can't (uint64_t has 64 bits of precision; a double
- * has only 52). If they can't be converted to a double, they are returned
- * as \ref QCBOR_TYPE_65BIT_NEG_INT.
- * In many cases, it will be reasonable to error out if the
- * number type returned here is \ref QCBOR_TYPE_65BIT_NEG_INT
- * on the assumption that many protocols will never uses these.
- * See also QCBOREncode_AddNegativeUInt64() for more discussion.
+ * Whole numbers from \c INT64_MAX +1 to \c UINT64_MAX will be
+ * returned as uint64_t indicated as \ref QCBOR_TYPE_UINT64, again
+ * including conversion of floating-point values that are whole
+ * numbers.
*
- * All others are returned as a double.
+ * Most other numbers are returned as a double as indicated by
+ * \ref QCBOR_TYPE_DOUBLE floating point with one set of exceptions.
*
- * This is useful for dCBOR which essentially combines floats
- * and integers into one number space.
+ * The exception is negative whole numbers in the range of -(2^63 + 1)
+ * to -(2^64) that have too much precision to be represented as a
+ * double. Doubles have only 52 bits of precision, so they can't
+ * precisely represent every whole integer in this range. CBOR can
+ * represent these values with 64-bits of precision and when this
+ * function encounters them they are returned as \ref
+ * QCBOR_TYPE_65BIT_NEG_INT. See the description of this type for
+ * instructions to gets its value. Also see
+ * QCBORDecode_BignumPreferred().
+ *
+ * To give an example, the value -18446744073709551616 can't be
+ * represented by an int64_t or uint64_t, but can be represented by a
+ * double so it is returned by this function as a double. The value
+ * -18446744073709551617 however can't be represented by a double
+ * because it has too much precision, so it is returned as \ref
+ * QCBOR_TYPE_65BIT_NEG_INT.
+ *
+ * This is useful for DCBOR which essentially combines floats and
+ * integers into one number space.
*
* Please see @ref Decode-Errors-Overview "Decode Errors Overview".
*
@@ -346,6 +358,68 @@
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
/**
+ * @brief Decode next item as a big number encoded using preferred serialization.
+ *
+ * @param[in] pCtx The decode context.
+ * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ * @param[in] BigNumBuf The buffer to write the result into.
+ * @param[in,out] pbIsNegative Set to true if the resulting big number is negative.
+ * @param[out] pBigNum The output big number.
+ *
+ * See QCBORDecode_PreferedBigNum() in full detail.
+ *
+ * The type processing rules are as follows.
+ *
+ * This always succeeds on type 0 and 1 integers (QCBOR_TYPE_INT64,
+ * QCBOR_TYPE_UINT64 and QCBOR_TYPE_65_BIT_NEG) no matter what
+ * uTagRequirement is. The rest of the rules pertain to what happens
+ * if the CBOR is not type 0 or type 1.
+ *
+ * If uTagRequirement is REQUIRE, this will fail on anything but a
+ * full and correct tag 2 or tag 3 big number.
+ *
+ * If uTagRequreiement is QCBOR_TAG_REQUIREMENT_NOT_A_TAG then this
+ * will fail on anything but a byte string.
+ *
+ * If QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, then this will succeed on
+ * either a byte string or a tag 2 or 3.
+ *
+ * If the item is a bare byte string, not a tag 2 or 3, then
+ * pbIsNegative is an input parameter that determines the sign of the
+ * big number. The sign must be known because the decoding of a
+ * positive big number is different than a negative.
+ *
+ * TODO: reevaluate whether to call this GetBigNum after tag redesign
+ * It would be more consistent to be GetBigNum with its friend named GetBigNumNoPreferred
+ * but the parameters are different so backwards compat can be with a mode bit.
+ */
+void
+QCBORDecode_GetBigNumPreferred(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pBigNum,
+ bool *pbIsNegative);
+
+void
+QCBORDecode_GetBigNumPreferredInMapN(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ int64_t nLabel,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pBigNum,
+ bool *pbIsNegative);
+
+void
+QCBORDecode_GetBigNumPreferredInMapSZ(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ const char *szLabel,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pBigNum,
+ bool *pbIsNegative);
+
+
+
+
+/**
* @brief Decode next item into a signed 64-bit integer with conversions.
*
* @param[in] pCtx The decode context.
@@ -1465,9 +1539,10 @@
*
* The negative value is computed as -1 - n, where n is the postive
* big number in @c pValue. There is no standard representation for
- * big numbers, positive or negative in C, so this implementation
+ * big numbers, positive or negative in C, so this function
* leaves it up to the caller to apply this computation for negative
- * big numbers.
+ * big numbers, but QCBORDecode_BignumPreferred() can be
+ * used too.
*
* See @ref Tag-Usage for discussion on tag requirements.
*
@@ -1479,14 +1554,13 @@
* then the protocol design must have some way of indicating the sign.
*
* See also QCBORDecode_GetInt64ConvertAll(),
- * QCBORDecode_GetUInt64ConvertAll() and
- * QCBORDecode_GetDoubleConvertAll() which can convert big numbers.
+ * QCBORDecode_GetUInt64ConvertAll(),
+ * QCBORDecode_GetDoubleConvertAll() and QCBORDecode_BignumPreferred() which can convert big numbers.
*
* See also @ref CBOR_TAG_POS_BIGNUM, @ref CBOR_TAG_NEG_BIGNUM,
* QCBOREncode_AddPositiveBignum(), QCBOREncode_AddNegativeBignum(),
* @ref QCBOR_TYPE_POSBIGNUM and @ref QCBOR_TYPE_NEGBIGNUM.
*/
-// Improvement: Add function that converts integers and other to big nums
void
QCBORDecode_GetBignum(QCBORDecodeContext *pCtx,
uint8_t uTagRequirement,
diff --git a/src/ieee754.c b/src/ieee754.c
index f9b7a3f..40421ad 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -691,6 +691,7 @@
/* Cast safe because of mask above; exponents < DOUBLE_EXPONENT_MAX */
const int64_t nDoubleUnbiasedExponent = (int64_t)uDoubleBiasedExponent - DOUBLE_EXPONENT_BIAS;
const uint64_t uDoubleSignificand = uDouble & DOUBLE_SIGNIFICAND_MASK;
+ const uint64_t bIsNegative = uDouble & DOUBLE_SIGN_MASK;
if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_ZERO) {
if(uDoubleSignificand == 0) {
@@ -706,13 +707,21 @@
/* --- NAN --- */
Result.type = IEEE754_ToInt_NaN; /* dCBOR doesn't care about payload */
} else {
- /* --- INIFINITY --- */
+ /* --- INFINITY --- */
Result.type = IEEE754_ToInt_NO_CONVERSION;
}
- } else if(nDoubleUnbiasedExponent < 0 ||
- (nDoubleUnbiasedExponent >= ((uDouble & DOUBLE_SIGN_MASK) ? 63 : 64))) {
+ } else if(nDoubleUnbiasedExponent < 0) {
/* --- Exponent out of range --- */
Result.type = IEEE754_ToInt_NO_CONVERSION;
+ } else if(nDoubleUnbiasedExponent >= 64) {
+ if(nDoubleUnbiasedExponent == 64 && uDoubleSignificand == 0 && bIsNegative) {
+ /* Very special case for -18446744073709551616.0 */
+ Result.integer.un_signed = 0; /* No negative 0, use it to indicate 2^64 */
+ Result.type = IEEE754_ToInt_IS_65BIT_NEG;
+ } else {
+ /* --- Exponent out of range --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ }
} else {
/* Conversion only fails when the input is too large or is not a
* whole number, never because of lack of precision because
@@ -737,10 +746,15 @@
/* Numbers greater than 2^52 with at most 52 significant bits */
uInteger <<= nDoubleUnbiasedExponent - DOUBLE_NUM_SIGNIFICAND_BITS;
}
- if(uDouble & DOUBLE_SIGN_MASK) {
+ if(bIsNegative) {
/* Cast safe because exponent range check above */
- Result.integer.is_signed = -((int64_t)uInteger);
- Result.type = IEEE754_ToInt_IS_INT;
+ if(nDoubleUnbiasedExponent == 63) {
+ Result.integer.un_signed = uInteger;
+ Result.type = IEEE754_ToInt_IS_65BIT_NEG;
+ } else {
+ Result.integer.is_signed = -((int64_t)uInteger);
+ Result.type = IEEE754_ToInt_IS_INT;
+ }
} else {
Result.integer.un_signed = uInteger;
Result.type = IEEE754_ToInt_IS_UINT;
@@ -769,6 +783,8 @@
/* 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 uint64_t bIsNegative = uSingle & SINGLE_SIGN_MASK;
+
if(nSingleUnbiasedExponent == SINGLE_EXPONENT_ZERO) {
if(uSingleleSignificand == 0 && !(uSingle & SINGLE_SIGN_MASK)) {
@@ -786,10 +802,18 @@
} else {
Result.type = IEEE754_ToInt_NO_CONVERSION;
}
- } else if(nSingleUnbiasedExponent < 0 ||
- (nSingleUnbiasedExponent >= ((uSingle & SINGLE_SIGN_MASK) ? 63 : 64))) {
+ } else if(nSingleUnbiasedExponent < 0) {
/* --- Exponent out of range --- */
Result.type = IEEE754_ToInt_NO_CONVERSION;
+ } else if(nSingleUnbiasedExponent >= 64) {
+ if(nSingleUnbiasedExponent == 64 && uSingleleSignificand == 0 && bIsNegative) {
+ /* Very special case for -18446744073709551616.0 */
+ Result.integer.un_signed = 0; /* No negative 0, use it to indicate 2^64 */
+ Result.type = IEEE754_ToInt_IS_65BIT_NEG;
+ } else {
+ /* --- Exponent out of range --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ }
} else {
/* Conversion only fails when the input is too large or is not a
* whole number, never because of lack of precision because
@@ -814,9 +838,15 @@
/* Numbers greater than 2^23 with at most 23 significant bits*/
uInteger <<= nSingleUnbiasedExponent - SINGLE_NUM_SIGNIFICAND_BITS;
}
- if(uSingle & SINGLE_SIGN_MASK) {
- Result.integer.is_signed = -((int64_t)uInteger);
- Result.type = IEEE754_ToInt_IS_INT;
+ if(bIsNegative) {
+ /* Cast safe because exponent range check above */
+ if(nSingleUnbiasedExponent == 63) {
+ Result.integer.un_signed = uInteger;
+ Result.type = IEEE754_ToInt_IS_65BIT_NEG;
+ } else {
+ Result.integer.is_signed = -((int64_t)uInteger);
+ Result.type = IEEE754_ToInt_IS_INT;
+ }
} else {
Result.integer.un_signed = uInteger;
Result.type = IEEE754_ToInt_IS_UINT;
@@ -837,31 +867,37 @@
uint64_t uDoubleSignificand;
int nPrecisionBits;
- /* Figure out the exponent and normalize the significand. This is
- * done by shifting out all leading zero bits and counting them. If
- * none are shifted out, the exponent is 63. */
- uDoubleSignificand = uInt;
- nDoubleUnbiasedExponent = 63;
- while(1) {
- if(uDoubleSignificand & 0x8000000000000000UL) {
- break;
+ if(uInt == 0) {
+ uDoubleSignificand = 0;
+ nDoubleUnbiasedExponent = DOUBLE_EXPONENT_ZERO;
+
+ } else {
+ /* Figure out the exponent and normalize the significand. This is
+ * done by shifting out all leading zero bits and counting them. If
+ * none are shifted out, the exponent is 63. */
+ uDoubleSignificand = uInt;
+ nDoubleUnbiasedExponent = 63;
+ while(1) {
+ if(uDoubleSignificand & 0x8000000000000000UL) {
+ break;
+ }
+ uDoubleSignificand <<= 1;
+ nDoubleUnbiasedExponent--;
+ };
+
+ /* Position significand correctly for a double. Only shift 63 bits
+ * because of the 1 that is present by implication in IEEE 754.*/
+ uDoubleSignificand >>= 63 - DOUBLE_NUM_SIGNIFICAND_BITS;
+
+ /* Subtract 1 which is present by implication in IEEE 754 */
+ uDoubleSignificand -= 1ULL << (DOUBLE_NUM_SIGNIFICAND_BITS);
+
+ nPrecisionBits = IEEE754_Private_CountPrecisionBits(uInt) - (64 - nDoubleUnbiasedExponent);
+
+ if(nPrecisionBits > DOUBLE_NUM_SIGNIFICAND_BITS) {
+ /* Will lose precision if converted */
+ return IEEE754_UINT_TO_DOUBLE_OOB;
}
- uDoubleSignificand <<= 1;
- nDoubleUnbiasedExponent--;
- };
-
- /* Position significand correctly for a double. Only shift 63 bits
- * because of the 1 that is present by implication in IEEE 754.*/
- uDoubleSignificand >>= 63 - DOUBLE_NUM_SIGNIFICAND_BITS;
-
- /* Subtract 1 which is present by implication in IEEE 754 */
- uDoubleSignificand -= 1ULL << (DOUBLE_NUM_SIGNIFICAND_BITS);
-
- nPrecisionBits = IEEE754_Private_CountPrecisionBits(uInt) - (64 - nDoubleUnbiasedExponent);
-
- if(nPrecisionBits > DOUBLE_NUM_SIGNIFICAND_BITS) {
- /* Will lose precision if converted */
- return IEEE754_UINT_TO_DOUBLE_OOB;
}
return IEEE754_AssembleDouble((uint64_t)uIsNegative,
diff --git a/src/ieee754.h b/src/ieee754.h
index c82b9b4..fed28f1 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -97,6 +97,7 @@
struct IEEE754_ToInt {
enum {IEEE754_ToInt_IS_INT,
IEEE754_ToInt_IS_UINT,
+ IEEE754_ToInt_IS_65BIT_NEG,
IEEE754_ToInt_NO_CONVERSION,
IEEE754_ToInt_NaN
} type;
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 05363f0..41e6b21 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -895,12 +895,18 @@
*
* CBOR doesn't explicitly specify two's compliment for integers but
* all CPUs use it these days and the test vectors in the RFC are
- * so. All integers in the CBOR structure are positive and the major
+ * so. All integers in encoded CBOR are unsigned and the CBOR major
* type indicates positive or negative. CBOR can express positive
- * integers up to 2^x - 1 where x is the number of bits and negative
- * integers down to 2^x. Note that negative numbers can be one more
- * away from zero than positive. Stdint, as far as I can tell, uses
- * two's compliment to represent negative integers.
+ * integers up to 2^64 - 1 negative integers down to -2^64. Note that
+ * negative numbers can be one more
+ * away from zero than positive because there is no negative zero.
+ *
+ * The "65-bit negs" are values CBOR can encode that can't fit
+ * into an int64_t or uint64_t. They decoded as a special type
+ * QCBOR_TYPE_65BIT_NEG_INT. Not that this type does NOT
+ * take into account the offset of one for CBOR negative integers.
+ * It must be applied to get the correct value. Applying this offset
+ * would overflow a uint64_t.
*/
static QCBORError
QCBOR_Private_DecodeInteger(const int nMajorType,
@@ -927,11 +933,8 @@
} else {
if(uArgument <= INT64_MAX) {
- /* CBOR's representation of negative numbers lines up with
- * the two-compliment representation. A negative integer has
- * one more in range than a positive integer. INT64_MIN is
- * equal to (-INT64_MAX) - 1.
- */
+ /* INT64_MIN is one further away from 0 than INT64_MAX
+ * so the -1 here doesn't overflow. */
pDecodedItem->val.int64 = (-(int64_t)uArgument) - 1;
pDecodedItem->uDataType = QCBOR_TYPE_INT64;
@@ -1169,6 +1172,8 @@
}
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+
#if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
static QCBORError
@@ -1256,8 +1261,7 @@
return QCBOR_SUCCESS;
}
-#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#else /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
static QCBORError
QCBORDecode_Private_SingleConformance(const float f, uint8_t uDecodeMode)
@@ -1280,11 +1284,9 @@
return QCBOR_SUCCESS;
}
}
-#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
-
/*
* Decode a float
*/
@@ -1318,7 +1320,6 @@
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
@@ -1349,12 +1350,10 @@
* 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;
@@ -1362,13 +1361,14 @@
if(uReturn != QCBOR_SUCCESS) {
break;
}
-#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
break;
}
return uReturn;
}
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+
/* Make sure #define value line up as DecodeSimple counts on this. */
@@ -1437,7 +1437,11 @@
case HALF_PREC_FLOAT: /* 25 */
case SINGLE_PREC_FLOAT: /* 26 */
case DOUBLE_PREC_FLOAT: /* 27 */
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
uReturn = QCBOR_Private_DecodeFloat(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
+#else
+ uReturn = QCBOR_ERR_ALL_FLOAT_DISABLED;
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
break;
case CBOR_SIMPLEV_FALSE: /* 20 */
@@ -1528,6 +1532,8 @@
uint64_t uArgument = 0;
int nAdditionalInfo = 0;
+ memset(pDecodedItem, 0, sizeof(QCBORItem));
+
/* Decode the "head" that every CBOR item has into the major type,
* argument and the additional info.
*/
@@ -1539,12 +1545,11 @@
&nMajorType,
&uArgument,
&nAdditionalInfo);
+
if(uReturn != QCBOR_SUCCESS) {
return uReturn;
}
- memset(pDecodedItem, 0, sizeof(QCBORItem));
-
/* All the functions below get inlined by the optimizer. This code
* is easier to read with them all being similar functions, even if
* some functions don't do much.
@@ -5297,7 +5302,7 @@
* numbers.
*/
static QCBORError
-QCBOR_Private_ProcessBigNum(const uint8_t uTagRequirement,
+QCBOR_Private_ProcessBigNum(const uint8_t uTagRequirement,
const QCBORItem *pItem,
UsefulBufC *pValue,
bool *pbIsNegative)
@@ -5336,6 +5341,7 @@
bool *pbIsNegative)
{
QCBORItem Item;
+
QCBORDecode_VGetNext(pMe, &Item);
if(pMe->uLastError) {
return;
@@ -6708,6 +6714,7 @@
case QCBOR_TYPE_65BIT_NEG_INT:
#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ // TODO: don't use float HW. We have the function to do it.
*pdValue = -(double)pItem->val.uint64 - 1;
break;
#else
@@ -7715,15 +7722,17 @@
{
QCBORItem Item;
struct IEEE754_ToInt ToInt;
- double d;
+ double dNum;
QCBORError uError;
if(pMe->uLastError != QCBOR_SUCCESS) {
return;
}
+ // TODO:VGetNext?
uError = QCBORDecode_GetNext(pMe, &Item);
if(uError != QCBOR_SUCCESS) {
+ *pNumber = Item;
pMe->uLastError = (uint8_t)uError;
return;
}
@@ -7772,15 +7781,25 @@
}
break;
-
case QCBOR_TYPE_65BIT_NEG_INT:
- d = IEEE754_UintToDouble(Item.val.uint64, 1);
- if(d == IEEE754_UINT_TO_DOUBLE_OOB) {
- *pNumber = Item;
- } else {
+ if(Item.val.uint64 == UINT64_MAX) {
+ /* The value -18446744073709551616 is encoded as an
+ * unsigned 18446744073709551615. It's a whole number that
+ * needs to be returned as a double. It can't be handled
+ * by IEEE754_UintToDouble because 18446744073709551616
+ * doesn't fit into a uint64_t. You can't get it by adding
+ * 1 to 18446744073709551615.
+ */
+ pNumber->val.dfnum = -18446744073709551616.0;
pNumber->uDataType = QCBOR_TYPE_DOUBLE;
- /* -1 is because of CBOR offset of negative numbers */
- pNumber->val.dfnum = d - 1;
+ } else {
+ dNum = IEEE754_UintToDouble(Item.val.uint64 + 1, 1);
+ if(dNum == IEEE754_UINT_TO_DOUBLE_OOB) {
+ *pNumber = Item;
+ } else {
+ pNumber->val.dfnum = dNum;
+ pNumber->uDataType = QCBOR_TYPE_DOUBLE;
+ }
}
break;
@@ -7792,3 +7811,317 @@
}
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+static UsefulBufC
+QCBORDecode_IntToBigNum(uint64_t uNum,
+ const UsefulBuf BigNumBuf)
+{
+ UsefulOutBuf OB;
+
+ /* With a UsefulOutBuf, there's no pointer math here. */
+ UsefulOutBuf_Init(&OB, BigNumBuf);
+
+ /* Must copy one byte even if zero. The loop, mask and shift
+ * algorithm provides endian conversion.
+ */
+ do {
+ UsefulOutBuf_InsertByte(&OB, uNum & 0xff, 0);
+ uNum >>= 8;
+ } while(uNum);
+
+ return UsefulOutBuf_OutUBuf(&OB);
+}
+
+
+static UsefulBufC
+QCBORDecode_Private_RemoveLeadingZeros(UsefulBufC String)
+{
+ while(String.len > 1) {
+ if(*(const uint8_t *)String.ptr) {
+ break;
+ }
+ String.len--;
+ String.ptr = (const uint8_t *)String.ptr + 1;
+ }
+
+ return String;
+}
+
+
+/* Add one to the big number and put the result in a new UsefulBufC
+ * from storage in UsefulBuf.
+ *
+ * Leading zeros must be removed before calling this.
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+static UsefulBufC
+QCBORDecode_BigNumCopyPlusOne(UsefulBufC BigNum,
+ UsefulBuf BigNumBuf)
+{
+ uint8_t uCarry;
+ uint8_t uSourceValue;
+ const uint8_t *pSource;
+ uint8_t *pDest;
+ ptrdiff_t uDestBytesLeft;
+
+ /* Start adding at the LSB */
+ pSource = &((const uint8_t *)BigNum.ptr)[BigNum.len-1];
+ pDest = &((uint8_t *)BigNumBuf.ptr)[BigNumBuf.len-1];
+
+ uCarry = 1; /* Gets set back to zero if add the next line doesn't wrap */
+ *pDest = *pSource + 1;
+ while(1) {
+ /* Wrap around from 0xff to 0 is a defined operation for
+ unsigned addition in C. */
+ if(*pDest != 0) {
+ /* The add operation didn't wrap so no more carry. This
+ * funciton only adds one, so when there is no more carry,
+ * carrying is over to the end.
+ */
+ uCarry = 0;
+ }
+
+ uDestBytesLeft = pDest - (uint8_t *)BigNumBuf.ptr;
+ if(pSource <= (const uint8_t *)BigNum.ptr && uCarry == 0) {
+ break; /* Successful exit */
+ }
+ if(pSource > (const uint8_t *)BigNum.ptr) {
+ uSourceValue = *--pSource;
+ } else {
+ /* All source bytes processed, but not the last carry */
+ uSourceValue = 0;
+ }
+
+ pDest--;
+ if(uDestBytesLeft < 0) {
+ return NULLUsefulBufC; /* Not enough space in destination buffer */
+ }
+
+ *pDest = uSourceValue + uCarry;
+ }
+
+ return (UsefulBufC){pDest, BigNumBuf.len - (size_t)uDestBytesLeft};
+}
+
+
+/* This returns 1 when uNum is 0 */
+static size_t
+QCBORDecode_Private_CountNonZeroBytes(uint64_t uNum)
+{
+ size_t uCount = 0;
+ do {
+ uCount++;
+ uNum >>= 8;
+ } while(uNum);
+
+ return uCount;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+QCBORError
+QCBORDecode_BignumPreferred(const QCBORItem Item,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pBigNum,
+ bool *pbIsNegative)
+{
+ QCBORError uResult;
+ size_t uLen;
+ UsefulBufC BigNum;
+ int uType;
+
+ uType = Item.uDataType;
+ if(uType == QCBOR_TYPE_BYTE_STRING) {
+ uType = *pbIsNegative ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM;
+ }
+
+ static const uint8_t Zero[] = {0x00};
+ BigNum = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(Zero);
+ if((uType == QCBOR_TYPE_POSBIGNUM || uType == QCBOR_TYPE_NEGBIGNUM) &&
+ Item.val.bigNum.len) {
+ BigNum = QCBORDecode_Private_RemoveLeadingZeros(Item.val.bigNum);
+ }
+
+ /* Compute required length so it can be returned if buffer is too small */
+ switch(uType) {
+ case QCBOR_TYPE_INT64:
+ uLen = QCBORDecode_Private_CountNonZeroBytes((uint64_t)(Item.val.int64 < 0 ? -Item.val.int64 : Item.val.int64));
+ break;
+
+ case QCBOR_TYPE_UINT64:
+ uLen = QCBORDecode_Private_CountNonZeroBytes(Item.val.uint64);
+ break;
+
+ case QCBOR_TYPE_65BIT_NEG_INT:
+ uLen = Item.val.uint64 == UINT64_MAX ? 9 : QCBORDecode_Private_CountNonZeroBytes(Item.val.uint64);
+ break;
+
+ case QCBOR_TYPE_POSBIGNUM:
+ uLen = BigNum.len;
+ break;
+
+ case QCBOR_TYPE_NEGBIGNUM:
+ uLen = BigNum.len;
+ if(UsefulBuf_IsValue(BigNum, 0xff) == SIZE_MAX) {
+ uLen++;
+ }
+ break;
+
+ default:
+ uLen = 0;
+ }
+
+ *pBigNum = (UsefulBufC){NULL, uLen};
+
+ if(BigNumBuf.len < uLen || uLen == 0 || BigNumBuf.ptr == NULL) {
+ return BigNumBuf.ptr == NULL ? QCBOR_SUCCESS : QCBOR_ERR_BUFFER_TOO_SMALL;
+ /* Buffer is too short or type is wrong */
+ }
+
+ uResult = QCBOR_SUCCESS;
+
+ if(uType == QCBOR_TYPE_POSBIGNUM) {
+ *pBigNum = UsefulBuf_Copy(BigNumBuf, BigNum);
+ *pbIsNegative = false;
+ } else if(uType == QCBOR_TYPE_UINT64) {
+ *pBigNum = QCBORDecode_IntToBigNum(Item.val.uint64, BigNumBuf);
+ *pbIsNegative = false;
+ } else if(uType == QCBOR_TYPE_INT64) {
+ *pbIsNegative = Item.val.int64 < 0;
+ *pBigNum = QCBORDecode_IntToBigNum((uint64_t)(*pbIsNegative ? -Item.val.int64 : Item.val.int64), BigNumBuf);
+ } else if(uType == QCBOR_TYPE_65BIT_NEG_INT) {
+ *pbIsNegative = true;
+ if(Item.val.uint64 == UINT64_MAX) {
+ /* The one value that can't be done with a computation
+ * because it would overflow a uint64_t*/
+ static const uint8_t TwoToThe64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ *pBigNum = UsefulBuf_Copy(BigNumBuf, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(TwoToThe64));
+ } else {
+ *pBigNum = QCBORDecode_IntToBigNum(Item.val.uint64 + 1, BigNumBuf);
+ }
+ } else if(uType == QCBOR_TYPE_NEGBIGNUM) {
+ /* The messy one. Take the stuff in the buffer and copy it to
+ * the new buffer, adding one to it. This might be one byte
+ * bigger than the original because of the carry from adding
+ * one.*/
+ *pbIsNegative = true;
+ *pBigNum = QCBORDecode_BigNumCopyPlusOne(BigNum, BigNumBuf);
+
+ } else {
+ uResult = QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+
+ return uResult;
+}
+
+
+static QCBORError
+QCBOR_Private_ProcessPreferredBigNum(const uint8_t uTagRequirement,
+ const QCBORItem *pItem,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pValue,
+ bool *pbIsNegative)
+{
+ if(pItem->uDataType != QCBOR_TYPE_INT64 &&
+ pItem->uDataType != QCBOR_TYPE_UINT64 &&
+ pItem->uDataType != QCBOR_TYPE_65BIT_NEG_INT) {
+
+ /* The integer types are always OK. If it's not an integer type drop
+ * in to the tag type checking system. */
+ const QCBOR_Private_TagSpec TagSpec =
+ {
+ uTagRequirement,
+ {QCBOR_TYPE_POSBIGNUM, QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+ {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+ };
+
+ QCBORError uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
+ if(uErr != QCBOR_SUCCESS) {
+ return uErr;
+ }
+ }
+
+ return QCBORDecode_BignumPreferred(*pItem, BigNumBuf, pValue, pbIsNegative);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetBigNumPreferred(QCBORDecodeContext *pMe,
+ const uint8_t uTagRequirement,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pValue,
+ bool *pbIsNegative)
+{
+ QCBORItem Item;
+
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ /* Already in error state, do nothing */
+ return;
+ }
+
+ QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+ if(uError != QCBOR_SUCCESS) {
+ pMe->uLastError = (uint8_t)uError;
+ return;
+ }
+
+ QCBOR_Private_ProcessPreferredBigNum(uTagRequirement, &Item, BigNumBuf, pValue, pbIsNegative);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetPreferredBignumInMapN(QCBORDecodeContext *pMe,
+ const int64_t nLabel,
+ const uint8_t uTagRequirement,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pValue,
+ bool *pbIsNegative)
+{
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ pMe->uLastError = (uint8_t)QCBOR_Private_ProcessPreferredBigNum(uTagRequirement,
+ &Item,
+ BigNumBuf,
+ pValue,
+ pbIsNegative);
+}
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetPreferredBignumInMapSZ(QCBORDecodeContext *pMe,
+ const char * szLabel,
+ const uint8_t uTagRequirement,
+ UsefulBuf BigNumBuf,
+ UsefulBufC *pValue,
+ bool *pbIsNegative)
+{
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ pMe->uLastError = (uint8_t)QCBOR_Private_ProcessPreferredBigNum(uTagRequirement,
+ &Item,
+ BigNumBuf,
+ pValue,
+ pbIsNegative);
+}
+
+
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 7d2f7fb..fa7f485 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -580,29 +580,6 @@
/*
- * Public functions for adding negative integers. See qcbor/qcbor_encode.h
- */
-void
-QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pMe, const uint64_t uValue)
-{
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- if(pMe->uMode >= QCBOR_ENCODE_MODE_DCBOR) {
- /* Never allowed in dCBOR */
- pMe->uError = QCBOR_ERR_NOT_PREFERRED;
- return;
- }
-
- if(!(pMe->uAllow & QCBOR_ENCODE_ALLOW_65_BIG_NEG)) {
- pMe->uError = QCBOR_ERR_NOT_ALLOWED;
- return;
- }
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-
- QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_NEGATIVE_INT, uValue, 0);
-}
-
-
-/*
* Public functions for adding signed integers. See qcbor/qcbor_encode.h
*/
void
@@ -679,6 +656,7 @@
IEEE754_union FloatResult;
bool bNoNaNPayload;
struct IEEE754_ToInt IntResult;
+ uint64_t uNegValue;
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
@@ -696,6 +674,16 @@
case IEEE754_ToInt_IS_UINT:
QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
return;
+ case IEEE754_ToInt_IS_65BIT_NEG:
+ {
+ if(IntResult.integer.un_signed == 0) {
+ uNegValue = UINT64_MAX;
+ } else {
+ uNegValue = IntResult.integer.un_signed-1;
+ }
+ QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
+ }
+ return;
case IEEE754_ToInt_NaN:
dNum = NAN;
bNoNaNPayload = true;
@@ -728,6 +716,7 @@
IEEE754_union FloatResult;
bool bNoNaNPayload;
struct IEEE754_ToInt IntResult;
+ uint64_t uNegValue;
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
if(IEEE754_SingleHasNaNPayload(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
@@ -745,6 +734,16 @@
case IEEE754_ToInt_IS_UINT:
QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
return;
+ case IEEE754_ToInt_IS_65BIT_NEG:
+ {
+ if(IntResult.integer.un_signed == 0) {
+ uNegValue = UINT64_MAX;
+ } else {
+ uNegValue = IntResult.integer.un_signed-1;
+ }
+ QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
+ }
+ return;
case IEEE754_ToInt_NaN:
fNum = NAN;
bNoNaNPayload = true;
@@ -764,6 +763,302 @@
+
+/* Actual addition of a positive/negative big num tag */
+static void
+QCBOREncode_Private_AddTBignum(QCBOREncodeContext *pMe,
+ const uint64_t uTag,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+ QCBOREncode_AddTag(pMe, uTag);
+ }
+ QCBOREncode_AddBytes(pMe, BigNum);
+}
+
+
+/* Add a positive/negative big num, non-preferred */
+static void
+QCBOREncode_Private_AddTBignumNoPreferred(QCBOREncodeContext *pMe,
+ const uint64_t uTag,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+#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 */
+
+ QCBOREncode_Private_AddTBignum(pMe, uTag, uTagRequirement, BigNum);
+}
+
+
+/* Is there a carry when you add 1 to the BigNum? */
+static bool
+QCBOREncode_Private_BigNumCarry(UsefulBufC BigNum)
+{
+ bool bCarry;
+ UsefulBufC SubBigNum;
+
+ if(BigNum.len == 0) {
+ return true; /* Adding one to zero-length string gives a carry */
+ } else {
+ SubBigNum = UsefulBuf_Tail(BigNum, 1);
+ bCarry = QCBOREncode_Private_BigNumCarry(SubBigNum);
+ if(*(const uint8_t *)BigNum.ptr == 0x00 && bCarry) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+
+/*
+ * Output negative bignum bytes with subtraction of 1
+ */
+void
+QCBOREncode_Private_AddTNegativeBignum(QCBOREncodeContext *pMe,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ size_t uLen;
+ bool bCarry;
+ bool bCopiedSomething;
+ uint8_t uByte;
+ UsefulBufC SubString;
+ UsefulBufC NextSubString;
+
+ if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+ QCBOREncode_AddTag(pMe, CBOR_TAG_NEG_BIGNUM);
+ }
+
+ /* This works on any length without the need of an additional buffer */
+
+ /* This subtracts one, possibly making the string shorter by one
+ * 0x01 -> 0x00
+ * 0x01 0x00 -> 0xff
+ * 0x00 0x01 0x00 -> 0x00 0xff
+ * 0x02 0x00 -> 0x01 0xff
+ * 0xff -> 0xfe
+ * 0xff 0x00 -> 0xfe 0xff
+ * 0x01 0x00 0x00 -> 0xff 0xff
+ */
+
+ /* Compute the length up front because it goes in the head */
+ bCarry = QCBOREncode_Private_BigNumCarry(UsefulBuf_Tail(BigNum, 1));
+ uLen = BigNum.len;
+ if(bCarry && *(const uint8_t *)BigNum.ptr >= 1 && BigNum.len > 1) {
+ uLen--;
+ }
+ QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uLen, 0);
+
+ SubString = BigNum;
+ bCopiedSomething = false;
+ while(SubString.len) {
+ uByte = *((const uint8_t *)SubString.ptr);
+ NextSubString = UsefulBuf_Tail(SubString, 1);
+ bCarry = QCBOREncode_Private_BigNumCarry(NextSubString);
+ if(bCarry) {
+ uByte--;
+ }
+ if(bCopiedSomething || NextSubString.len == 0 || uByte != 0) { /* No leading zeros, but one zero is OK */
+ UsefulOutBuf_AppendByte(&(pMe->OutBuf), uByte);
+ bCopiedSomething = true;
+ }
+ SubString = NextSubString;
+ }
+}
+
+
+static UsefulBufC
+QCBOREncode_Private_RemoveLeadingZeros(UsefulBufC String)
+{
+ while(String.len > 1) {
+ if(*(const uint8_t *)String.ptr) {
+ break;
+ }
+ String.len--;
+ String.ptr = (const uint8_t *)String.ptr + 1;
+ }
+
+ return String;
+}
+
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTNegativeBignumNoPreferred(QCBOREncodeContext *pMe,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+#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 */
+
+ if(UsefulBuf_IsValue(BigNum, 0) == SIZE_MAX) {
+ pMe->uError = QCBOR_ERR_NO_NEGATIVE_ZERO;
+ return;
+ }
+
+ if(pMe->uConfig & QCBOR_ENCODE_CONFIG_V1_COMPAT) {
+ QCBOREncode_Private_AddTBignum(pMe, CBOR_TAG_NEG_BIGNUM, uTagRequirement, BigNum);
+ } else {
+ QCBOREncode_Private_AddTNegativeBignum(pMe, uTagRequirement, QCBOREncode_Private_RemoveLeadingZeros(BigNum));
+ }
+}
+
+
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMap(QCBOREncodeContext *pMe,
+ const char *szLabel,
+ uint8_t uTagRequirement,
+ UsefulBufC BigNumber)
+{
+ QCBOREncode_AddSZString(pMe, szLabel);
+ QCBOREncode_AddTNegativeBignumNoPreferred(pMe, uTagRequirement, BigNumber);
+}
+
+
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMapN(QCBOREncodeContext *pMe,
+ int64_t nLabel,
+ uint8_t uTagRequirement,
+ UsefulBufC BigNumber)
+{
+ QCBOREncode_AddInt64(pMe, nLabel);
+ QCBOREncode_AddTNegativeBignumNoPreferred(pMe, uTagRequirement, BigNumber);
+}
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTPositiveBignumNoPreferred(QCBOREncodeContext *pMe,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ QCBOREncode_Private_AddTBignumNoPreferred(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNum);
+}
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMap(QCBOREncodeContext *pMe,
+ const char *szLabel,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ QCBOREncode_AddSZString(pMe, szLabel);
+ QCBOREncode_Private_AddTBignumNoPreferred(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNum);
+}
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMapN(QCBOREncodeContext *pMe,
+ int64_t nLabel,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ QCBOREncode_AddInt64(pMe, nLabel);
+ QCBOREncode_Private_AddTBignumNoPreferred(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNum);
+}
+
+
+
+
+/* This will return an erroneous value if BigNum.len > 8 */
+/* Convert from bignum to uint with endianess conversion */
+static uint64_t
+QCBOREncode_Private_BigNumToUInt(const UsefulBufC BigNum)
+{
+ uint64_t uInt;
+ size_t uIndex;
+
+ uInt = 0;
+ for(uIndex = 0; uIndex < BigNum.len; uIndex++) {
+ uInt = (uInt << 8) + ((const uint8_t *)BigNum.ptr)[uIndex];
+ }
+
+ return uInt;
+}
+
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pMe,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ if(pMe->uConfig & QCBOR_ENCODE_CONFIG_V1_COMPAT) {
+ QCBOREncode_AddTPositiveBignumNoPreferred(pMe, uTagRequirement, BigNum);
+ } else {
+ const UsefulBufC BigNumNLZ = QCBOREncode_Private_RemoveLeadingZeros(BigNum);
+ if(BigNumNLZ.len <= 8) {
+ /* Preferred serialization requires conversion to type 0 */
+ QCBOREncode_AddUInt64(pMe, QCBOREncode_Private_BigNumToUInt(BigNumNLZ));
+ } else {
+ QCBOREncode_Private_AddTBignum(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNumNLZ);
+ }
+ }
+}
+
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pMe,
+ const uint8_t uTagRequirement,
+ const UsefulBufC BigNum)
+{
+ uint64_t uInt;
+ bool bIs2exp64;
+ static const uint8_t twoExp64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ if(UsefulBuf_IsValue(BigNum, 0) == SIZE_MAX) {
+ pMe->uError = QCBOR_ERR_NO_NEGATIVE_ZERO;
+ return;
+ }
+
+ if(pMe->uConfig & QCBOR_ENCODE_CONFIG_V1_COMPAT) {
+ QCBOREncode_AddTNegativeBignumNoPreferred(pMe, uTagRequirement, BigNum);
+
+ } else {
+ /* Here we do preferred serialization. That requires removal of leading zeros */
+ const UsefulBufC BigNumNLZ = QCBOREncode_Private_RemoveLeadingZeros(BigNum);
+
+ bIs2exp64 = ! UsefulBuf_Compare(BigNumNLZ, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(twoExp64));
+
+ if(BigNumNLZ.len <= 8 || bIs2exp64) {
+ /* Must convert to CBOR type 1, a negative integer */
+ if(bIs2exp64) {
+ /* 2^64 is a 9 byte big number. Since negative numbers are offset
+ * by one in CBOR, it can be encoded as a type 1 negative. The
+ * conversion below won't work because the uInt will overflow
+ * before the subtraction of 1.
+ */
+ uInt = UINT64_MAX;
+ } else {
+ uInt = QCBOREncode_Private_BigNumToUInt(BigNumNLZ);
+ uInt--; /* CBOR's negative offset of 1 */
+ }
+ QCBOREncode_AddNegativeUInt64(pMe, uInt);
+
+ } else {
+ QCBOREncode_Private_AddTNegativeBignum(pMe, uTagRequirement, BigNumNLZ);
+ }
+ }
+}
+
+
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/**
* @brief Semi-private method to add bigfloats and decimal fractions.
@@ -798,10 +1093,10 @@
void
QCBOREncode_Private_AddExpMantissa(QCBOREncodeContext *pMe,
const uint64_t uTag,
+ const int64_t nExponent,
const UsefulBufC BigNumMantissa,
const bool bBigNumIsNegative,
- const int64_t nMantissa,
- const int64_t nExponent)
+ const int64_t nMantissa)
{
/* This is for encoding either a big float or a decimal fraction,
* both of which are an array of two items, an exponent and a
diff --git a/test/float_tests.c b/test/float_tests.c
index ce078d4..d59241f 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -281,10 +281,10 @@
{"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
{"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x3B\x7F\xFF\xFF\xFF\xFF\xFF\xFB\xFF", 9}},
- /* -18446742974197924000.0.0 -- large negative that converts to float, but too large for int64 */
- {-18446742974197924000.0, -18446742974197924000.0f,
+ /* -18446742974197923840.0 -- large negative that converts to float, but too large for int64 */
+ {-18446742974197923840.0, -18446742974197923840.0f,
{"\xFA\xDF\x7F\xFF\xFF", 5}, {"\xFB\xC3\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
- {"\xFA\xDF\x7F\xFF\xFF", 5}, {"\xFA\xDF\x7F\xFF\xFF", 5}},
+ {"\xFA\xDF\x7F\xFF\xFF", 5}, {"\x3B\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF", 9}},
/* 3.4028234663852886E+38 -- largest possible single */
{3.4028234663852886E+38, 3.40282347E+38f,
@@ -414,7 +414,7 @@
for(uTestIndex = 0; FloatTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
pTestCase = &FloatTestCases[uTestIndex];
- if(uTestIndex == 34) {
+ if(uTestIndex == 40) {
uDecoded = 1;
}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 191c448..202be19 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -79,8 +79,11 @@
return (int32_t)uCode;
}
+
/*
[
+ -18446744073709551616,
+ -18446744073709551615,
-9223372036854775808,
-4294967297,
-4294967296,
@@ -175,13 +178,13 @@
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
return (int32_t)nCBORError;
if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
- Item.val.uint64 != 18446744073709551615ULL)
+ Item.val.uint64 != 0xffffffffffffffff)
return -1;
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
return (int32_t)nCBORError;
if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
- Item.val.uint64 != 18446744073709551614ULL)
+ Item.val.uint64 != 0xfffffffffffffffe)
return -1;
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
@@ -2558,7 +2561,7 @@
}
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
- if(nIndex == 54) {
+ if(nIndex == 55) {
uCBORError = 9; /* For setting break points */
}
@@ -4648,15 +4651,155 @@
0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
+
+struct BignumDecodeTest {
+ const char *szDescription;
+ UsefulBufC Encoded;
+ QCBORError uErr;
+ UsefulBufC ExpectedBigNum;
+ bool bExpectedSign;
+};
+
#ifndef QCBOR_DISABLE_TAGS
+static struct BignumDecodeTest BignumDecodeTests[] = {
+ {
+ "-18446744073709551617",
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+ true
+ },
+ {
+ "-18446744073709551616 preferred",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ true
+ },
+ {
+ "-18446744073709551616 as big num",
+ {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ true
+ },
+ {
+ "-9223372036854775808 -(2^63)",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ {"\x80\x00\x00\x00\x00\x00\x00\x00", 8},
+ true
+ },
+
+ {
+ "Preferred -1",
+ {"\x20", 1},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ true
+ },
+ {
+ "bignum -1",
+ {"\xc3\x42\x00\x00", 4},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ true
+ },
+ {
+ "bignum -1 empty buffer",
+ {"\xc3\x40", 2},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ true
+ },
+ {
+ "Preferred Zero",
+ {"\x00", 1},
+ QCBOR_SUCCESS,
+ {"\x00", 1},
+ false
+ },
+ {
+ "Bignum zero",
+ {"\xC2\x40", 2},
+ QCBOR_SUCCESS,
+ {"\x00", 1},
+ false
+ },
+ {
+ "Bignum zero with leading zeros",
+ {"\xC2\x43\x00\x00\x00", 5},
+ QCBOR_SUCCESS,
+ {"\x00", 1},
+ false
+ },
+ {
+ "Preferred one",
+ {"\x01", 1},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ false
+ },
+ {
+ "Bignum one",
+ {"\xc2\x41\x01", 3},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ false
+ },
+ {
+ "512 with leading zeros",
+ {"\x1A\x00\x00\x02\x00", 5},
+ QCBOR_SUCCESS,
+ {"\x02\x00", 2},
+ false
+ },
+ {
+ "512 with leading zeros again",
+ {"\xc2\x46\x00\x00\x00\x00\x02\x00", 8},
+ QCBOR_SUCCESS,
+ {"\x02\x00", 2},
+ false
+ },
+ {
+ "1297093868730187896 (a byte pattern with zeros and different digits",
+ {"\x1B\x12\x00\x34\x00\x56\x00\x00\x78", 9},
+ QCBOR_SUCCESS,
+ {"\x12\x00\x34\x00\x56\x00\x00\x78", 8},
+ false
+ },
+ {
+ "Preferred UINT64_MAX",
+ {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+ false
+ },
+ {
+ "Bignum UINT64_MAX",
+ {"\xC2\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ QCBOR_SUCCESS,
+ {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+ false
+ },
+ {
+ "UINT64_MAX + 1",
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ false
+ }
+};
+
+
/* The expected big num */
static const uint8_t spBigNum[] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00};
-#endif /* QCBOR_DISABLE_TAGS */
+#endif /* ! QCBOR_DISABLE_TAGS */
-int32_t BignumParseTest(void)
+int32_t BignumDecodeTest(void)
{
QCBORDecodeContext DCtx;
QCBORItem Item;
@@ -4732,10 +4875,72 @@
UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
return -14;
}
-
-
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
+ unsigned uTestIndex;
+ unsigned uTestCount;
+ struct BignumDecodeTest *pTest;
+ QCBORError uErr;
+ UsefulBuf_MAKE_STACK_UB( BignumBuf, 200);
+ UsefulBufC ResultBigNum;
+ bool bIsNeg;
+
+ uTestCount = (int)C_ARRAY_COUNT(BignumDecodeTests, struct BignumDecodeTest);
+
+ for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+ pTest = &BignumDecodeTests[uTestIndex];
+
+ if(uTestIndex == 13) {
+ bIsNeg = false; /* Line of code so a break point can be set. */
+ }
+
+ QCBORDecode_Init(&DCtx, pTest->Encoded, 0);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 1, uErr);
+ }
+
+ uErr = QCBORDecode_BignumPreferred(Item, BignumBuf, &ResultBigNum, &bIsNeg);
+ if(uErr != pTest->uErr) {
+ return MakeTestResultCode(uTestIndex, 2, uErr);
+ }
+
+ if(uErr != QCBOR_SUCCESS) {
+ continue; /* This test passed */
+ }
+
+ if(UsefulBuf_Compare(ResultBigNum, pTest->ExpectedBigNum)) {
+ return MakeTestResultCode(uTestIndex, 3, 0);
+ }
+
+ if(bIsNeg != pTest->bExpectedSign) {
+ return MakeTestResultCode(uTestIndex, 4, 0);
+ }
+
+ uErr = QCBORDecode_BignumPreferred(Item, (UsefulBuf){NULL, 200}, &ResultBigNum, &bIsNeg);
+ if(ResultBigNum.len != pTest->ExpectedBigNum.len) {
+ return MakeTestResultCode(uTestIndex, 5, uErr);
+ }
+
+ QCBORDecode_Init(&DCtx, pTest->Encoded, 0);
+ QCBORDecode_GetBigNumPreferred(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, BignumBuf, &ResultBigNum, &bIsNeg);
+ uErr = QCBORDecode_GetError(&DCtx);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 6, uErr);
+ }
+
+ if(UsefulBuf_Compare(ResultBigNum, pTest->ExpectedBigNum)) {
+ return MakeTestResultCode(uTestIndex, 7, 0);
+ }
+
+ if(bIsNeg != pTest->bExpectedSign) {
+ return MakeTestResultCode(uTestIndex, 8, 0);
+ }
+
+ }
+
+
#else
if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_TAGS_DISABLED) {
@@ -7732,6 +7937,10 @@
return (int32_t)(3333+nIndex);
}
+ if(nIndex == 21) {
+ uInt = 99; // For break point only
+ }
+
int64_t nInt;
QCBORDecode_GetInt64ConvertAll(&DCtx, 0xffff, &nInt);
if(QCBORDecode_GetError(&DCtx) != pF->uErrorInt64) {
@@ -10109,7 +10318,7 @@
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 -5000;
}
return ProcessDecodeFailures(DecodeConformanceFailures,
@@ -10187,39 +10396,98 @@
{0, UINT64_MAX, 0}
},
{
- "INT64_MIN",
+ "Largest float that is also representable as int64_t",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xfb\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {-9223372036854774784, 0, 0}
+ },
+ {
+ "-9223372036854775807",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xfe", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {-9223372036854775807, 0, 0}
+ },
+ {
+ "Largest representable in int64_t (INT64_MIN)",
{"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
QCBOR_SUCCESS,
QCBOR_TYPE_INT64,
{INT64_MIN, 0, 0}
},
{
+ "-9223372036854775809 First encoded as 65-bit neg",
+ {"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 0, 0}
+ },
+
+ {
+ "-9223372036854777856 First float not representable as int64_t",
+ {"\x3B\x80\x00\x00\x00\x00\x00\x07\xFF", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -9223372036854777856.0}
+ },
+
+ {
"18446742974197923840",
{"\xFB\x43\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
QCBOR_SUCCESS,
QCBOR_TYPE_UINT64,
{0, 18446742974197923840ULL, 0}
},
+
{
- "65-bit neg, too much precision",
- {"\x3B\x80\x00\x00\x00\x00\x00\x00\x01", 9},
- QCBOR_SUCCESS,
- QCBOR_TYPE_65BIT_NEG_INT,
- {0, 0x8000000000000001, 0}
- },
- {
- "65-bit neg lots of precision",
- {"\x3B\xff\xff\xff\xff\xff\xff\xf0\x00", 9},
+ "-18446744073709547522 65-bit neg lots of precision",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xef\xff", 9},
QCBOR_SUCCESS,
QCBOR_TYPE_DOUBLE,
- {0, 0, -18446744073709547521.0}
+ {0, 0, -18446744073709547522.0}
},
+
+ {
+ "-18446744073709549568 Next to largest float encodable as 65-bit neg",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xf7\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709549568.0}
+ },
+
+ {
+ "-18446744073709551616 Largest possible encoded 65-bit neg",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709551616.0}
+ },
+ {
+ "-18446744073709551617 First value representable only as a tag 3 big num",
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+#ifndef QCBOR_DISABLE_TAGS
+ QCBOR_ERR_UNEXPECTED_TYPE,
+#else
+ QCBOR_ERR_TAGS_DISABLED,
+#endif /* ! QCBOR_DISABLE_TAGS */
+ QCBOR_TYPE_NONE,
+ {0, 0, -0}
+ },
+ {
+ "-18446744073709555712 First whole integer that must be encoded as float in DCBOR",
+ {"\xFB\xC3\xF0\x00\x00\x00\x00\x00\x01", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709555712.0}
+ },
+
{
"65-bit neg very precise",
{"\x3B\xff\xff\xff\xff\xff\xff\xf8\x00", 9},
QCBOR_SUCCESS,
- QCBOR_TYPE_DOUBLE,
- {0, 0, -18446744073709549569.0}
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 18446744073709549568ULL, 0.0}
},
{
"65-bit neg too precise",
@@ -10232,8 +10500,8 @@
"65-bit neg, power of two",
{"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 9},
QCBOR_SUCCESS,
- QCBOR_TYPE_DOUBLE,
- {0, 0, -9223372036854775809.0}
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 9223372036854775808ULL, 0.0}
},
{
"Zero",
@@ -10260,20 +10528,20 @@
int32_t
-PreciseNumbersTest(void)
+PreciseNumbersDecodeTest(void)
{
- int i;
+ unsigned uTestIndex;
+ unsigned uTestCount;
QCBORError uErr;
QCBORItem Item;
QCBORDecodeContext DCtx;
const struct PreciseNumberConversion *pTest;
- const int count = (int)C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+ uTestCount = C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+ for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+ pTest = &PreciseNumberConversions[uTestIndex];
- for(i = 0; i < count; i++) {
- pTest = &PreciseNumberConversions[i];
-
- if(i == 11) {
+ if(uTestIndex == 18) {
uErr = 99; // For break point only
}
@@ -10284,11 +10552,11 @@
uErr = QCBORDecode_GetError(&DCtx);
if(uErr != pTest->uError) {
- return i * 1000 + (int)uErr;
+ return MakeTestResultCode(uTestIndex, 1, uErr);
}
if(pTest->qcborType != Item.uDataType) {
- return i * 1000 + 200;
+ return MakeTestResultCode(uTestIndex, 2, 0);
}
if(pTest->qcborType == QCBOR_TYPE_NONE) {
@@ -10298,25 +10566,25 @@
switch(pTest->qcborType) {
case QCBOR_TYPE_INT64:
if(Item.val.int64 != pTest->number.int64) {
- return i * 1000 + 300;
+ return MakeTestResultCode(uTestIndex, 3, 0);
}
break;
case QCBOR_TYPE_UINT64:
- case QCBOR_TYPE_65BIT_NEG_INT:
+ case QCBOR_TYPE_NEGBIGNUM:
if(Item.val.uint64 != pTest->number.uint64) {
- return i * 1000 + 400;
+ return MakeTestResultCode(uTestIndex, 4, 0);
}
break;
case QCBOR_TYPE_DOUBLE:
if(isnan(pTest->number.d)) {
if(!isnan(Item.val.dfnum)) {
- return i * 1000 + 600;
+ return MakeTestResultCode(uTestIndex, 5, 0);
}
} else {
if(Item.val.dfnum != pTest->number.d) {
- return i * 1000 + 500;
+ return MakeTestResultCode(uTestIndex, 6, 0);
}
}
break;
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index a6d5837..31d9e48 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -185,7 +185,7 @@
/*
Parse some big numbers, positive and negative
*/
-int32_t BignumParseTest(void);
+int32_t BignumDecodeTest(void);
/*
@@ -324,7 +324,7 @@
*/
int32_t DecodeConformanceTests(void);
-int32_t PreciseNumbersTest(void);
+int32_t PreciseNumbersDecodeTest(void);
int32_t ErrorHandlingTests(void);
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 7645475..88251ac 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -128,6 +128,18 @@
}
+static inline int32_t
+MakeTestResultCode(uint32_t uTestCase,
+ uint32_t uTestNumber,
+ QCBORError uErrorCode)
+{
+ uint32_t uCode = (uTestCase * 1000000) +
+ (uTestNumber * 1000) +
+ (uint32_t)uErrorCode;
+ return (int32_t)uCode;
+}
+
+
// One big buffer that is used by all the tests to encode into
// Putting it in uninitialized data is better than using a lot
// of stack. The tests should run on small devices too.
@@ -276,10 +288,10 @@
static const uint8_t spExpectedEncodedAll[] = {
- 0x98, 0x24, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
+ 0x98, 0x23, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
0x64, 0x1a, 0x05, 0x5d, 0x23, 0x15, 0x65, 0x49, 0x4e, 0x54,
0x36, 0x34, 0xd8, 0x4c, 0x1b, 0x00, 0x00, 0x00, 0x12, 0x16,
- 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0x20, 0xa4, 0x63, 0x4c, 0x42,
+ 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0xa4, 0x63, 0x4c, 0x42,
0x4c, 0x18, 0x4d, 0x23, 0x18, 0x58, 0x78, 0x1a, 0x4e, 0x45,
0x47, 0x4c, 0x42, 0x4c, 0x54, 0x48, 0x41, 0x54, 0x20, 0x49,
0x53, 0x20, 0x4b, 0x49, 0x4e, 0x44, 0x20, 0x4f, 0x46, 0x20,
@@ -476,11 +488,11 @@
0x71, 0x47, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x20, 0x69, 0x73,
0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0xf5, 0x19,
0x10, 0x41, 0xf5, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x63, 0x42, 0x4E, 0x2B,
0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
+ 0x01, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
0x3F, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
@@ -523,7 +535,6 @@
QCBOREncode_AddInt64(pECtx, 77689989909);
QCBOREncode_AddUInt64(pECtx, 0);
QCBOREncode_AddInt64(pECtx, -44);
- QCBOREncode_AddNegativeUInt64(pECtx, 0);
/* ints that go in maps */
QCBOREncode_OpenMap(pECtx);
@@ -672,7 +683,7 @@
QCBOREncode_CloseMap(pECtx);
/* Big numbers */
- static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
const UsefulBufC BIGNUM = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBignum);
QCBOREncode_AddPositiveBignum(pECtx, BIGNUM);
QCBOREncode_AddNegativeBignum(pECtx, BIGNUM);
@@ -736,13 +747,6 @@
#else
uExpectedErr = QCBOR_SUCCESS;
#endif
- /* Test the QCBOR_ERR_NOT_ALLOWED error codes */
- QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
- QCBOREncode_AddNegativeUInt64(&ECtx, 1);
- if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
- nReturn = -21;
- goto Done;
- }
#if !defined(QCBOR_DISABLE_ENCODE_USAGE_GUARDS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
@@ -944,6 +948,131 @@
return(nReturn);
}
+struct BigNumEncodeTest {
+ const char *szDescription;
+ UsefulBufC BigNum;
+ /* Expect all to succeed; no special error codes needed */
+ UsefulBufC PositiveNoPreferred;
+ UsefulBufC PositivePreferred;
+ UsefulBufC NegativeNoPreferred;
+ UsefulBufC NegativePreferred;
+};
+
+struct BigNumEncodeTest BigNumEncodeTestCases[] = {
+ {
+ "2^96 -1 or 79228162514264337593543950335 pos and neg with leading zeros",
+ {"\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 15},
+ {"\xC2\x4F\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 17},
+ {"\xC2\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 14},
+ {"\xC3\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", 14},
+ {"\xC3\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", 14},
+ },
+
+ {
+ "2^64+1 or 18446744073709551617 pos and neg)",
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x01", 11},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x01", 11},
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ },
+ {
+ "2^64 or 18446744073709551616 pos and neg)",
+ {"\x01\x00\x00\x00\x0000\x00\x00\x00", 9},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ },
+ {
+ "2^64 - 1 or 18446744073709551615 pos and neg",
+ {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+ {"\xC2\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xfe", 10},
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xfe", 9},
+ },
+ {
+ "1 and -1",
+ {"\x01", 1},
+ {"\xC2\x41\x01", 3},
+ {"\x01", 1},
+ {"\xC3\x41\x00", 3},
+ {"\x20", 1},
+ },
+ {
+ "0 and error",
+ {"\x00", 1},
+ {"\xC2\x41\x00", 3},
+ {"\x00", 1},
+ NULLUsefulBufC,
+ NULLUsefulBufC,
+ },
+ {
+ "leading zeros -- 0 and error",
+ {"\x00\x00\x00\x00", 4},
+ {"\xC2\x44\x00\x00\x00\x00", 6},
+ {"\x00", 1},
+ NULLUsefulBufC,
+ NULLUsefulBufC,
+ }
+
+};
+
+
+int32_t BigNumEncodeTests(void)
+{
+ unsigned uTestIndex;
+ unsigned uTestCount;
+ QCBOREncodeContext Enc;
+ UsefulBufC B;
+ const struct BigNumEncodeTest *pTest;
+
+ uTestCount = (int)C_ARRAY_COUNT(BigNumEncodeTestCases, struct BigNumEncodeTest);
+
+ for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+ pTest = &BigNumEncodeTestCases[uTestIndex];
+
+ if(uTestIndex == 3) {
+ B.len = 0; /* Line of code so a break point can be set. */
+ }
+
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddTPositiveBignumNoPreferred(&Enc, QCBOR_ENCODE_AS_TAG, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->PositiveNoPreferred)) {
+ return MakeTestResultCode(uTestIndex, 1, QCBOR_SUCCESS);
+ }
+
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddPositiveBignum (&Enc, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->PositivePreferred)) {
+ return MakeTestResultCode(uTestIndex, 2, QCBOR_SUCCESS);
+ }
+
+ if(!UsefulBuf_IsNULLC(pTest->NegativeNoPreferred)){
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddTNegativeBignumNoPreferred(&Enc, QCBOR_ENCODE_AS_TAG, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->NegativeNoPreferred)) {
+ return MakeTestResultCode(uTestIndex, 3, QCBOR_SUCCESS);
+ }
+ }
+
+ if(!UsefulBuf_IsNULLC(pTest->NegativePreferred)){
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddNegativeBignum(&Enc, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->NegativePreferred)) {
+ return MakeTestResultCode(uTestIndex, 4, QCBOR_SUCCESS);
+ }
+ }
+ }
+
+ return 0;
+}
+
/*
85 # array(5)
@@ -2765,12 +2894,12 @@
0x07, 0x08, 0x09, 0x10, 0xC4, 0x82, 0x1B, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3,
0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x10, 0xC5, 0x82, 0x19, 0x01, 0x2C,
+ 0x08, 0x09, 0x0F, 0xC5, 0x82, 0x19, 0x01, 0x2C,
0x18, 0x64, 0xC5, 0x82, 0x33, 0xC2, 0x4A, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02,
- 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F};
/*
@@ -2809,10 +2938,10 @@
0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0xC4, 0x82,
0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x10, 0x19, 0x01, 0xF4,
+ 0x06, 0x07, 0x08, 0x09, 0x0F, 0x19, 0x01, 0xF4,
0xC4, 0x82, 0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03,
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x69,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F, 0x69,
0x62, 0x69, 0x67, 0x20, 0x66, 0x6C, 0x6F, 0x61,
0x74, 0xC5, 0x82, 0x19, 0x01, 0x2C, 0x18, 0x64,
0x19, 0x02, 0x58, 0xC5, 0x82, 0x19, 0x01, 0x2C,
@@ -2829,18 +2958,153 @@
0x20, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76,
0x65, 0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02,
- 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F,
0x19, 0x03, 0x20, 0xC5, 0x82, 0x3B, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x10
+ 0x09, 0x0F
};
-int32_t ExponentAndMantissaEncodeTests(void)
+struct EAMEncodeTest {
+ const char *szDescription;
+ int64_t nExponent;
+ UsefulBufC BigNumMantissa;
+ int64_t nMantissa;
+ bool bSign;
+ bool bv1Mode;
+ enum {EAM_Any, EAM_Pref, EAM_CDE} eSerialization;
+ // TODO: add tag requirement
+
+ /* Only testing successes (right?) */
+ UsefulBufC BigFloat;
+ UsefulBufC DecFrac;
+ UsefulBufC BigFloatBig;
+ UsefulBufC DecFracBig;
+};
+
+struct EAMEncodeTest EET[] = {
+ { "basic",
+ -1,
+ NULLUsefulBufC,
+ 3,
+ false,
+ false,
+ EAM_Pref,
+
+ {"\xC5\x82\x20\x03", 4},
+ {"\xC4\x82\x20\x03", 4},
+ NULLUsefulBufC,
+ NULLUsefulBufC
+ },
+
+ { "bignum gets preferred",
+ -1,
+ {"\x00\x03",2},
+ 0,
+ false,
+ false,
+ EAM_Pref,
+
+ NULLUsefulBufC,
+ NULLUsefulBufC,
+ {"\xC5\x82\x20\x03", 4},
+ {"\xC4\x82\x20\x03", 4},
+ }
+
+ // TODO: add more test cases, including converting some of the already-existing
+};
+
+
+
+static void
+EAMTestSetup(const struct EAMEncodeTest *pTest, QCBOREncodeContext *pEnc)
+{
+ QCBOREncode_Init(pEnc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ if(pTest->bv1Mode) {
+ QCBOREncode_Setv1Compatibility(pEnc);
+ }
+
+ switch(pTest->eSerialization) {
+ case EAM_Pref:
+ QCBOREncode_SerializationPreferred(pEnc);
+ break;
+ case EAM_CDE:
+ QCBOREncode_SerializationCDE(pEnc);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+int32_t
+ExponentAndMantissaEncodeTests(void)
{
QCBOREncodeContext EC;
UsefulBufC EncodedExponentAndMantissa;
+ int nIndex;
+ QCBORError uErr;
+
+ const int nNumberOfTests = C_ARRAY_COUNT(EET, struct EAMEncodeTest);
+
+ for(nIndex = 0; nIndex < nNumberOfTests; nIndex++) {
+ struct EAMEncodeTest *pTest = &EET[nIndex];
+
+
+ if(UsefulBuf_IsNULLC(pTest->BigNumMantissa)) {
+ EAMTestSetup(pTest, &EC);
+
+ QCBOREncode_AddDecimalFraction(&EC, pTest->nMantissa, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 1, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->DecFrac)) {
+ return MakeTestResultCode((uint32_t)nIndex, 2, 0);
+ }
+
+ EAMTestSetup(pTest, &EC);
+ QCBOREncode_AddBigFloat(&EC, pTest->nMantissa, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->BigFloat)) {
+ return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+ }
+
+ } else {
+ EAMTestSetup(pTest, &EC);
+
+ QCBOREncode_AddDecimalFractionBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->DecFracBig)) {
+ return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+ }
+
+ EAMTestSetup(pTest, &EC);
+
+ QCBOREncode_AddBigFloatBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->BigFloatBig)) {
+ return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+ }
+ }
+ }
+
// Constant for the big number used in all the tests.
static const uint8_t spBigNum[] = {0x01, 0x02, 0x03, 0x04, 0x05,
@@ -2861,9 +3125,11 @@
return -2;
}
+ struct UBCompareDiagnostic Foo;
+
int nReturn = UsefulBuf_CompareWithDiagnostic(EncodedExponentAndMantissa,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentAndMantissaArray),
- NULL);
+ &Foo);
if(nReturn) {
return nReturn;
}
@@ -2933,9 +3199,7 @@
return -3;
}
-
struct UBCompareDiagnostic Diag;
-
nReturn = UsefulBuf_CompareWithDiagnostic(EncodedExponentAndMantissa,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentAndMantissaMap),
&Diag);
@@ -3650,6 +3914,9 @@
QCBOREncode_AddInt64ToMap(&EC, "a", 1);
QCBOREncode_AddDoubleToMap(&EC, "x", 2.0);
QCBOREncode_AddDoubleToMap(&EC, "r", 3.4028234663852886E+38);
+ QCBOREncode_AddDoubleToMap(&EC, "d1", -18446744073709549568.0);
+ QCBOREncode_AddDoubleToMap(&EC, "d2", -18446744073709551616.0);
+ QCBOREncode_AddDoubleToMap(&EC, "d3", -18446744073709555712.0);
QCBOREncode_AddDoubleToMap(&EC, "b", NAN);
QCBOREncode_CloseMap(&EC);
@@ -3657,9 +3924,7 @@
QCBOREncode_Finish(&EC, &Encoded);
static const uint8_t spExpecteddCBOR[] = {
- 0xA5, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E,
- 0x00, 0x61, 0x6B, 0x01, 0x61, 0x72, 0xFA, 0x7F,
- 0x7F, 0xFF, 0xFF, 0x61, 0x78, 0x02};
+ 0xA8, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E, 0x00, 0x61, 0x6B, 0x01, 0x61, 0x72, 0xFA, 0x7F, 0x7F, 0xFF, 0xFF, 0x61, 0x78, 0x02, 0x62, 0x64, 0x31, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0x62, 0x64, 0x32, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x62, 0x64, 0x33, 0xFB, 0xC3, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpecteddCBOR),
Encoded)) {
@@ -3704,13 +3969,6 @@
return 102;
}
- /* 65-bit negative integers */
- QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
- QCBOREncode_SerializationdCBOR(&EC);
- QCBOREncode_AddNegativeUInt64(&EC, 1);
- if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
- return 103;
- }
/* Improvement: when indefinite length string encoding is supported
* test it here too. */
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index 5e2f978..1be5635 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -94,9 +94,9 @@
/*
- Encodes a goodly number of floats and doubles and checks encoding is right
+ * Big number encoding tests.
*/
-int32_t FloatValuesTest1(void);
+int32_t BigNumEncodeTests(void);
/*
diff --git a/test/run_tests.c b/test/run_tests.c
index 3fe4905..682d5e1 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -67,6 +67,7 @@
static test_entry s_tests[] = {
+ TEST_ENTRY(BigNumEncodeTests),
#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
TEST_ENTRY(DecodeConformanceTests),
#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
@@ -81,8 +82,6 @@
#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
TEST_ENTRY(SpiffyDateDecodeTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
- TEST_ENTRY(ErrorHandlingTests),
- TEST_ENTRY(OpenCloseBytesTest),
TEST_ENTRY(EnterBstrTest),
TEST_ENTRY(IntegerConvertTest),
TEST_ENTRY(EnterMapTest),
@@ -114,7 +113,7 @@
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY(BasicEncodeTest),
TEST_ENTRY(NestedMapTest),
- TEST_ENTRY(BignumParseTest),
+ TEST_ENTRY(BignumDecodeTest),
#ifndef QCBOR_DISABLE_TAGS
TEST_ENTRY(OptTagParseTest),
@@ -142,7 +141,7 @@
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
TEST_ENTRY(FloatValuesTests),
- TEST_ENTRY(PreciseNumbersTest),
+ TEST_ENTRY(PreciseNumbersDecodeTest),
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
TEST_ENTRY(GeneralFloatEncodeTests),
TEST_ENTRY(GeneralFloatDecodeTests),