Merge from master
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index e43bf04..34bdfc7 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -178,6 +178,7 @@
E78C91DF240C90C100F4CECE /* qcbor_common.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_common.h; path = inc/qcbor/qcbor_common.h; sourceTree = "<group>"; tabWidth = 3; };
E78C91E0240C90C100F4CECE /* qcbor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_private.h; path = inc/qcbor/qcbor_private.h; sourceTree = "<group>"; tabWidth = 3; };
E78C91E1240C90C100F4CECE /* qcbor_encode.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_encode.h; path = inc/qcbor/qcbor_encode.h; sourceTree = "<group>"; tabWidth = 3; };
+ E7A7B60E2B76FB62009102C2 /* Serialization.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = Serialization.md; path = doc/Serialization.md; sourceTree = "<group>"; };
E7C960B72800A09E00FB537C /* ub-example.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ub-example.h"; sourceTree = "<group>"; };
E7C960B82800A09E00FB537C /* ub-example.c */ = {isa = PBXFileReference; indentWidth = 3; lastKnownFileType = sourcecode.c.c; path = "ub-example.c"; sourceTree = "<group>"; tabWidth = 3; };
E7FDBF16256C969D007138A8 /* QCBOR_Disable_Indef */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR_Disable_Indef; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -246,6 +247,7 @@
children = (
E776E161214EE19C00E67947 /* README.md */,
E743D132251014E60017899F /* Tagging.md */,
+ E7A7B60E2B76FB62009102C2 /* Serialization.md */,
E776E096214AE0C700E67947 /* cmd_line_main.c */,
E776E092214AE07C00E67947 /* inc */,
E776E08B214AE06600E67947 /* src */,
@@ -274,8 +276,8 @@
E776E08C214AE07400E67947 /* qcbor_encode.c */,
E776E08E214AE07500E67947 /* qcbor_decode.c */,
E776E08D214AE07500E67947 /* UsefulBuf.c */,
- E73B57582161CA690080D658 /* ieee754.c */,
E73B57572161CA680080D658 /* ieee754.h */,
+ E73B57582161CA690080D658 /* ieee754.c */,
E7864765252CE63100A0C11B /* qcbor_err_to_str.c */,
);
name = src;
diff --git a/README.md b/README.md
index 5160cb9..9adfefe 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,18 @@
+This is the QCBOR 2.0 branch. It is under development and not ready for use.
+
+The plan for QCBOR 2 is a few larger features:
+ - Map sorting
+ - Full canonical encoding support (CDE)
+ - Improved support for tag handling
+ - Perhaps other features like OID support
+ - Perhaps dCBOR support
+ - Improved test and verification
+ - Improved documentation
+ - Better presence in GitHub
+
+The improved tag handling is one of the largest work items and may change
+backwards compatibility.
+

**QCBOR** is a powerful, commercial-quality CBOR encoder-decoder that
@@ -323,23 +338,23 @@
defining this is to remove dependency on floating point hardware and
libraries.
+
#### #define QCBOR_DISABLE_PREFERRED_FLOAT
-This eliminates support for half-precision
-and CBOR preferred serialization by disabling
-QCBOR's shift and mask based implementation of
-half-precision floating-point.
+This eliminates support of:
+- encode/decode of half-precision
+- shortest-form encoding of floats
+- QCBORDecode_GetNumberConvertPrecisely()
-With this defined, single and double-precision floating-point
-numbers can still be encoded and decoded. Conversion
-of floating-point to and from integers, big numbers and
-such is also supported. Floating-point dates are still
-supported.
+This saves about 1KB of object code, though much of this can be saved
+by not calling any functions to encode doubles or floats or
+QCBORDecode_GetNumberConvertPrecisely
-The primary reason to define this is to save object code.
-Roughly 900 bytes are saved, though about half of this
-can be saved just by not calling any functions that
-encode floating-point numbers.
+With this defined, single and double-precision floating-point numbers
+can still be encoded and decoded. Some conversion of floating-point to
+and from integers, big numbers and such is also supported. Floating-point
+dates are still supported.
+
#### #define USEFULBUF_DISABLE_ALL_FLOAT
@@ -485,7 +500,7 @@
QCBOR_DISABLE_NON_INTEGER_LABELS causes any label that doesn't
fit in an int64_t to result in a QCBOR_ERR_MAP_LABEL_TYPE error.
-This also disables QCBOR_DECODE_MODE_MAP_AS_ARRAY and
+This also disables QCBOR_DECODE_MODE_MAP_AS_ARRAY and
QCBOR_DECODE_MODE_MAP_STRINGS_ONLY. It is fairly common for CBOR-based
protocols to use only small integers as labels.
diff --git a/doc/Serialization.md b/doc/Serialization.md
new file mode 100644
index 0000000..8637e47
--- /dev/null
+++ b/doc/Serialization.md
@@ -0,0 +1,8 @@
+@file Serialization.md
+
+@anchor Serialization
+
+# Serialization and Determinism
+
+To be filled in...
+
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 3b7afd7..218d269 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -44,6 +44,10 @@
when who what, where, why
-------- ---- --------------------------------------------------
10/05/2024 llundblade Add Xxx_OffsetToPointer.
+ 28/02/2024 llundblade Rearrange UsefulOutBuf_Compare().
+ 19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
+ 19/11/2023 llundblade Add UsefulOutBuf_Swap().
+ 19/11/2023 llundblade Add UsefulOutBuf_Compare().
19/12/2022 llundblade Document that adding empty data is allowed.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf.
9/21/2021 llundbla Clarify UsefulOutBuf size calculation mode
@@ -866,7 +870,7 @@
/**
- * @brief Initialize and supply the actual output buffer.
+ * @brief Initialize and supply the output buffer.
*
* @param[out] pUOutBuf The @ref UsefulOutBuf to initialize.
* @param[in] Storage Buffer to output into.
@@ -1348,7 +1352,7 @@
/**
- * @brief Returns the resulting valid data in a UsefulOutBuf
+ * @brief Returns the data put into a UsefulOutBuf.
*
* @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
*
@@ -1366,7 +1370,7 @@
/**
- * @brief Copies the valid data into a supplied buffer
+ * @brief Copy out the data put into a UsefulOutBuf.
*
* @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
* @param[out] Dest The destination buffer to copy into.
@@ -1376,11 +1380,105 @@
* state was entered.
*
* This is the same as UsefulOutBuf_OutUBuf() except it copies the
- * data to @c Dest.
+ * data to @c Dest rather than returning a pointer.
*/
UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *pUOutBuf, UsefulBuf Dest);
+/**
+ * @brief Returns data starting at an offset that was put into a UsefulOutBuf.
+ *
+ * @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
+ * @param[in] uOffset Offset to bytes to return.
+ *
+ * @return NULLUsefulBufC or the bytes at the offset.
+ *
+ * This is the same as UsefulOutBuf_OutUBuf() except a starting offset
+ * maybe specified. It returns the bytes starting at @c uOffset to the
+ * end of what was encoded so far. Calling this with @c uOffset 0 is
+ * equivalent to UsefulOutBuf_OutUBuf().
+ *
+ * If there's nothing at @c uOffset or it is past the in the output
+ * buffer, a \ref NULLUsefulBufC is returned.
+ *
+ * This is typically not needed in typical use. It is used by QCBOR
+ * along with UsefulOutBuf_Compare() and UsefulOutBuf_Swap() for
+ * sorting CBOR maps.
+ */
+UsefulBufC
+UsefulOutBuf_OutUBufOffset(UsefulOutBuf *pUOutBuf, size_t uOffset);
+
+
+/**
+ * @brief Compare bytes at offsets.
+ *
+ * @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
+ * @param[in] uStart1 Offset of first bytes to compare.
+ * @param[in] uLen1 Length of first bytes to compare.
+ * @param[in] uStart2 Offset of second bytes to compare.
+ * @param[in] uLen2 Length of second bytes to compare.
+ *
+ * @return 0 for equality, positive if uStart1 is lexographically larger,
+ * negative if uStart2 is lexographically larger.
+ *
+ * This looks into bytes that have been output at the offsets @c start1
+ * and @c start2. It compares bytes at those two starting points until
+ * they are not equal or @c uLen1 or @c uLen2 is reached. If the
+ * length of the string given is off the end of the output data, the
+ * string will be effectively concated to the data in the output
+ * buffer for the comparison.
+ *
+ * This returns positive when @c uStart1 lexographically sorts ahead
+ * of @c uStart2 and vice versa. Zero is returned if the strings
+ * compare equally.
+ *
+ * If lengths are unequal and the first bytes are an exact subset of
+ * the second string, then a positve value will be returned and vice
+ * versa.
+ *
+ * If either start is past the end of data in the output buffer, 0
+ * will be returned. It is the caller's responsibility to make sure
+ * the offsets are not off the end so that a comparison is actually
+ * being made. No data will ever be read off the end of the buffer so
+ * this safe no matter what offsets are passed.
+ *
+ * This is a relatively odd function in that it works on data in the
+ * output buffer. It is employed by QCBOR to sort CBOR-encoded maps
+ * that are in the output buffer.
+ */
+int UsefulOutBuf_Compare(UsefulOutBuf *pUOutBuf,
+ size_t uStart1, size_t uLen1,
+ size_t uStart2, size_t uLen2);
+
+/**
+ * @brief Swap two regions of output bytes.
+ *
+ * @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
+ * @param[in] uStartOffset Offset to start of bytes to be swapped.
+ * @param[in] uPivotOffset Offset to pivot around which bytes are swapped.
+ * @param[in] uEndOffset Offset to end of region to be swappe.
+ *
+ * This reaches into bytes that have been output and swaps two
+ * adjacent regions.
+ *
+ * If any of the offsets are outside the range of valid data, no
+ * swapping will be performed. If the start is not the smallest and
+ * the pivot is not in the middle no swapping will be performed.
+ *
+ * The byte at @c uStartOffset will participate in the swapping. The
+ * byte at @c uEndOffset will not participate in the swapping, only
+ * the byte before it.
+ *
+ * This is a relatively odd function in that it works on data in the
+ * output buffer. It is employed by QCBOR to bubble sort encoded CBOR
+ * maps.
+ */
+void UsefulOutBuf_Swap(UsefulOutBuf *pUOutBuf,
+ size_t uStartOffset,
+ size_t uPivotOffset,
+ size_t uEndOffset);
+
+
/**
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 20643ab..d64e973 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -44,6 +44,7 @@
#endif
+
/**
* @file qcbor_common.h
*
@@ -60,8 +61,8 @@
* - QCBOR 1.1 is indicated by the #define QCBOR_1_1
* - QCBOR 1.0 is indicated by the absence of all the above
*/
-#define QCBOR_VERSION_MAJOR 1
-#define QCBOR_VERSION_MINOR 3
+#define QCBOR_VERSION_MAJOR 2
+#define QCBOR_VERSION_MINOR 0
#define QCBOR_VERSION_PATCH 0
@@ -407,7 +408,7 @@
* this error is returned. This error is unrecoverable because the
* built-in tag decoding doesn't try to consume the unexpected
* type. In previous versions of QCBOR this was considered a
- * recoverable error hence @ref QCBOR_ERR_BAD_TAG_CONTENT. Going
+ * recoverable error hence QCBOR_ERR_BAD_TAG_CONTENT. Going
* back further, RFC 7049 use the name "optional tags". That name
* is no longer used because "optional" was causing confusion. See
* also @ref QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT. */
@@ -523,10 +524,21 @@
* (to save object code). */
QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT = 78,
+ /** Attempt to output non-preferred, non-CDE or non-dCBOR when not
+ * allowed by mode. See QCBOREncode_SerializationPreferred(),
+ * QCBOREncode_SerializationCDE(),
+ * QCBOREncode_SerializationdCBOR().
+ */
+ QCBOR_ERR_NOT_PREFERRED = 79,
+
+ /* Trying to encode something that is discouraged (e.g., 65-bit
+ * negative integer) without allowing it by calling
+ * QCBOREncode_Allow() */
+ QCBOR_ERR_NOT_ALLOWED = 80,
/** QCBORDecode_EnterBstrWrapped() cannot be used on
* indefinite-length strings because they exist in memory pool for
* a @ref QCBORStringAllocate. */
- QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING = 79,
+ QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING = 81,
/** A range of error codes that can be made use of by the
* caller. QCBOR internally does nothing with these except notice
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 918d162..c1f37cf 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -316,6 +316,16 @@
/** Type for a double floating-point number. Data is in @c val.dfnum. */
#define QCBOR_TYPE_DOUBLE 27
+/** Special type for integers between -2^63 - 1 to -2^64 that
+ * can't be returned as @ref QCBOR_TYPE_INT64 because they don't fit
+ * in an int64_t. The value is returned in @c val.uint64, but this
+ * isn't the number transmitted. Do this arithmatic (carefully to
+ * avoid over/underflow) to get the value transmitted: - val.uint64 - 1.
+ * See QCBOREncode_AddNegativeUInt64() for a longer explanation
+ * and warning. */
+#define QCBOR_TYPE_65BIT_NEG_INT 28
+
+
#define QCBOR_TYPE_BREAK 31 /* Used internally; never returned */
/** For @ref QCBOR_DECODE_MODE_MAP_AS_ARRAY decode mode, a map that is
@@ -463,9 +473,7 @@
/** The value for @c uDataType @ref QCBOR_TYPE_DAYS_EPOCH -- the
* number of days before or after Jan 1, 1970. */
int64_t epochDays;
- /** No longer used. Was the value for @ref QCBOR_TYPE_DATE_STRING,
- * but now that value is in @c string. This will be removed in QCBOR 2.0. */
- UsefulBufC dateString;
+
/** The value for @c uDataType @ref QCBOR_TYPE_POSBIGNUM and
* @ref QCBOR_TYPE_NEGBIGNUM. */
UsefulBufC bigNum;
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 83c167e..c72f4d9 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -437,7 +437,7 @@
* is too small, encoding will go into an error state and not write
* anything further.
*
- * If allocating on the stack the convenience macro
+ * If allocating on the stack, the convenience macro
* UsefulBuf_MAKE_STACK_UB() can be used, but its use is not required.
*
* Since there is no reallocation or such, the output buffer must be
@@ -471,6 +471,144 @@
/**
+ * @brief Select preferred serialization mode.
+ *
+ * @param[in] pCtx The encoding context for mode set.
+ *
+ * Setting this mode will cause QCBOR to return an error if an attempt
+ * is made to use one of the methods that produce non-preferred
+ * serialization. It doesn't change anything else as QCBOR produces
+ * preferred serialization by default.
+ *
+ * The non-preferred methods are: QCBOREncode_AddFloatNoPreferred(),
+ * QCBOREncode_AddDoubleNoPreferred(),
+ * QCBOREncode_OpenArrayIndefiniteLength(),
+ * QCBOREncode_CloseArrayIndefiniteLength(),
+ * QCBOREncode_OpenMapIndefiniteLength(),
+ * QCBOREncode_CloseMapIndefiniteLength(), plus those derived from the
+ * above listed.
+ *
+ * This mode is just a user guard to prevent accidentally calling
+ * something that produces non-preferred serialization. It doesn't do
+ * anything but causes errors to occur on attempts to call the above
+ * listed functions. This does nothing if the library is compiled
+ * QCBOR_DISABLE_ENCODE_USAGE_GUARDS.
+ *
+ * See @ref Serialization. It is usually not necessary to set this
+ * mode, but there is usually no disadvantage to setting it. Preferred
+ * Serialization is defined in RFC 8949, section 4.1.
+ */
+static void
+QCBOREncode_SerializationPreferred(QCBOREncodeContext *pCtx);
+
+
+/**
+ * @brief Select CBOR deterministic encoding mode.
+ *
+ * @param[in] pCtx The encoding context for mode set.
+
+ * This causes QCBOR to produce CBOR Deterministic Encoding (CDE).
+ * With CDE, two distant unrelated CBOR encoders will produce exactly
+ * the same encoded CBOR for a given input.
+ *
+ * In addition to doing everything
+ * QCBOREncode_SerializationPreferred() does (including exclusion of
+ * indefinite lengths), this causes maps to be sorted. The map is
+ * sorted automatically when QCBOREncode_CloseMap() is called.
+ * QCBOREncode_CloseMap() becomes equivalent to
+ * QCBOREncode_CloseAndSortMap().
+ *
+ * Note that linking this function causese about 30% more code from
+ * the QCBOR library to be linked. Also, QCBOREncode_CloseMap() runs
+ * slower, but this is probably only of consequence in very
+ * constrained environments.
+ *
+ * See @ref Serialization. It is usually not necessary to set this
+ * mode as determinism is very rarely needed. However it will
+ * usually work with most protocols. CDE is defined in
+ * draft-ietf-cbor-cde.
+ */
+static void
+QCBOREncode_SerializationCDE(QCBOREncodeContext *pCtx);
+
+
+/**
+ * @brief Select "dCBOR" encoding mode.
+ *
+ * @param[in] pCtx The encoding context for mode set.
+ *
+ * This is a superset of CDE. This function does everything
+ * QCBOREncode_SerializationCDE() does. Also it is a super set of
+ * preferred serialization and does everything
+ * QCBOREncode_SerializationPreferred() does.
+ *
+ * The main feature of dCBOR is that there is only one way to serialize a
+ * particular numeric value. This changes the behavior of functions
+ * that add floating-point numbers. If the floating-point number is
+ * whole, it will be encoded as an integer, not a floating-point number.
+ * 0.000 will be encoded as 0x00. Precision is never lost in this
+ * conversion.
+ *
+ * dCBOR also disallows NaN payloads. QCBOR will allow NaN payloads if
+ * you pass a NaN to one of the floating-point encoding functions.
+ * This mode forces all NaNs to the half-precision queit NaN. Also see
+ * QCBOREncode_Allow().
+ *
+ * dCBOR 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().
+ *
+ * See @ref Serialization. Set this mode only if the protocol you are
+ * implementing requires dCBOR. This mode is usually not compatible
+ * with protocols that don't use dCBOR. dCBOR is defined in
+ * draft-mcnally-deterministic-cbor.
+ */
+static void
+QCBOREncode_SerializationdCBOR(QCBOREncodeContext *pCtx);
+
+
+
+
+/** Bit flag to be passed to QCBOREncode_Allow() to allow NaN payloads
+ * to be output by QCBOREncode_AddDouble(),
+ * QCBOREncode_AddDoubleNoPreferred(), QCBORENcode_AddFloat() and
+ * QCBOREncode_AddSingleleNoPreferred. */
+#define QCBOR_ENCODE_ALLOW_NAN_PAYLOAD 0x01
+
+/** Bit flag to be passed to QCBOREncode_Allow() 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. */
+#define QCBOR_ENCODE_ALLOW_ALL 0xFF
+
+
+/**
+ * @brief Allow encoding of less-interoperable values.
+ *
+ * @param[in] pCtx The encoding context.
+ * @param[in] uAllow Bit flags indicating what to allow.
+ *
+ * There are a few things in the CBOR standard that are often not
+ * supported and are thus not very interoperable. By default QCBOR
+ * will error if you attempt to output them. This disables that
+ * error.
+ *
+ * See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD and
+ * @ref QCBOR_ENCODE_ALLOW_65_BIG_NEG.
+ *
+ * This does nothing if the library is compiled
+ * QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+static void
+QCBOREncode_Allow(QCBOREncodeContext *pCtx, uint8_t uAllow);
+
+
+
+/**
* @brief Add a signed 64-bit integer to the encoded output.
*
* @param[in] pCtx The encoding context to add the integer to.
@@ -546,6 +684,55 @@
/**
+ * @brief Add a negative 64-bit integer to encoded output
+ *
+ * @param[in] pCtx The encoding context to add the integer to.
+ * @param[in] uNum The integer to add.
+ *
+ * QCBOREncode_AddInt64() is much better to encode negative integers
+ * than this. What this can do is add integers with one more
+ * significant bit than an int64_t (a "65-bit" integer if you count
+ * 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
+ * this so all negative values CBOR can represent can be encoded by
+ * QCBOR (making this a complete CBOR implementation).
+ *
+ * The most negative value QCBOREncode_AddInt64() can encode is
+ * -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
+ * positive or negative 18446744073709551616 in any standard C data
+ * type.
+ *
+ * Negative integers are normally decoded in QCBOR with type
+ * @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
+ * 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
+QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pCtx, uint64_t uNum);
+
+static void
+QCBOREncode_AddNegativeUInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, uint64_t uNum);
+
+static void
+QCBOREncode_AddNegativeUInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, uint64_t uNum);
+
+/**
* @brief Add a UTF-8 text string to the encoded output.
*
* @param[in] pCtx The encoding context to add the text to.
@@ -648,6 +835,9 @@
*
* 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.
*/
static void
QCBOREncode_AddDouble(QCBOREncodeContext *pCtx, double dNum);
@@ -687,8 +877,8 @@
* @param[in] pCtx The encoding context to add the double to.
* @param[in] dNum The double-precision number to add.
*
- * This always outputs the number as a 64-bit double-precision.
- * Preferred serialization is not used.
+ * Output a double-precision float straight-through with no checking or
+ * processing for preferred serializtion, dCBOR or other.
*
* Error handling is the same as QCBOREncode_AddInt64().
*
@@ -711,8 +901,8 @@
* @param[in] pCtx The encoding context to add the double to.
* @param[in] fNum The single-precision number to add.
*
- * This always outputs the number as a 32-bit single-precision.
- * Preferred serialization is not used.
+ * Output a single-precision float straight-through with no checking or
+ * processing for preferred serializtion, dCBOR or other.
*
* Error handling is the same as QCBOREncode_AddInt64().
*
@@ -2025,10 +2215,35 @@
* This is the same as QCBOREncode_CloseMap(), but the open map that
* is being close must be of indefinite length.
*/
-static void
+static void
QCBOREncode_CloseMapIndefiniteLength(QCBOREncodeContext *pCtx);
+/**
+ * @brief Close and sort an open map.
+ *
+ * @param[in] pCtx The encoding context to close the map in .
+ *
+ * This is the same as QCBOREncode_CloseMap() except it sorts the map
+ * per RFC 8949 Section 4.2.1 and checks for duplicate map keys. This
+ * sort is lexicographic of the CBOR-encoded map labels.
+ *
+ * This is more expensive than most things in the encoder. It uses
+ * bubble sort which runs in n-squared time where @c n is the number
+ * of map items. Sorting large maps on slow CPUs might be slow. This
+ * is also increases the object code size of the encoder by about 30%
+ * (500-1000 bytes).
+ *
+ * Bubble sort was selected so as to not need require configuration of
+ * a buffer to track map item offsets. Bubble sort works well even
+ * though map items are not all the same size because it always swaps
+ * adjacent items.
+ */
+void
+QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pCtx);
+
+void
+QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pCtx);
/**
@@ -2390,6 +2605,7 @@
int64_t nExponent);
+
/**
* @brief Semi-private method to add simple items and floating-point.
*
@@ -2414,50 +2630,48 @@
}
-/**
- * @brief Semi-private method to add only the type and length of a byte string.
- *
- * @param[in] pCtx The context to initialize.
- * @param[in] Bytes Pointer and length of the input data.
- *
- * This will be removed in QCBOR 2.0. It was never a public function.
- *
- * This is the same as QCBOREncode_AddBytes() except it only adds the
- * CBOR encoding for the type and the length. It doesn't actually add
- * the bytes. You can't actually produce correct CBOR with this and
- * the rest of this API. It is only used for a special case where the
- * valid CBOR is created manually by putting this type and length in
- * and then adding the actual bytes. In particular, when only a hash
- * of the encoded CBOR is needed, where the type and header are hashed
- * separately and then the bytes is hashed. This makes it possible to
- * implement COSE Sign1 with only one copy of the payload in the
- * output buffer, rather than two, roughly cutting memory use in half.
- *
- * This is only used for this odd case, but this is a supported
- * tested function for QCBOR 1.0.
- *
- * See also QCBOREncode_EncodeHead().
- */
-static void
-QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pCtx,
- UsefulBufC Bytes);
-
-static void
-QCBOREncode_AddBytesLenOnlyToMap(QCBOREncodeContext *pCtx,
- const char *szLabel,
- UsefulBufC Bytes);
-
-static void
-QCBOREncode_AddBytesLenOnlyToMapN(QCBOREncodeContext *pCtx,
- int64_t nLabel,
- UsefulBufC Bytes);
-
-
/* Forward declaration */
static void
QCBOREncode_AddSZString(QCBOREncodeContext *pMe, const char *szString);
+static inline void
+QCBOREncode_SerializationCDE(QCBOREncodeContext *pMe)
+{
+ /* The use of a function pointer here is a little trick to reduce
+ * code linked for the common use cases that don't sort. If this
+ * function is never linked, then QCBOREncode_CloseAndSortMap() is
+ * never linked and the amount of code pulled in is small. If the
+ * mode switch between sorting and not sorting were an if
+ * statement, then QCBOREncode_CloseAndSortMap() would always be
+ * linked even when not used. */
+ pMe->pfnCloseMap = QCBOREncode_CloseAndSortMap;
+ pMe->uMode = QCBOR_ENCODE_MODE_CDE;
+}
+
+static inline void
+QCBOREncode_SerializationdCBOR(QCBOREncodeContext *pMe)
+{
+ pMe->pfnCloseMap = QCBOREncode_CloseAndSortMap;
+ pMe->uMode = QCBOR_ENCODE_MODE_DCBOR;
+}
+
+static inline void
+QCBOREncode_SerializationPreferred(QCBOREncodeContext *pMe)
+{
+ pMe->uMode = QCBOR_ENCODE_MODE_PREFERRED;
+}
+
+static inline void
+QCBOREncode_Allow(QCBOREncodeContext *pMe, const uint8_t uAllow)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ pMe->uAllow = uAllow;
+#else
+ (void)uAllow;
+ (void)pMe;
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+}
static inline void
@@ -2506,6 +2720,24 @@
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_AddNegativeUInt64(pMe, uNum);
+}
+
+static inline void
+QCBOREncode_AddNegativeUInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint64_t uNum)
+{
+ QCBOREncode_AddInt64(pMe, nLabel);
+ QCBOREncode_AddNegativeUInt64(pMe, uNum);
+}
+
+
+static inline void
QCBOREncode_AddText(QCBOREncodeContext *pMe, const UsefulBufC Text)
{
QCBOREncode_Private_AddBuffer(pMe, CBOR_MAJOR_TYPE_TEXT_STRING, Text);
@@ -2817,35 +3049,6 @@
}
-/*
- * Public functions for adding only a byte string length. See qcbor/qcbor_encode.h
- */
-static inline void
-QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pMe, const UsefulBufC Bytes)
-{
- QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, Bytes.len, 0);
-}
-
-
-static inline void
-QCBOREncode_AddBytesLenOnlyToMap(QCBOREncodeContext *pMe,
- const char *szLabel,
- const UsefulBufC Bytes)
-{
- QCBOREncode_AddSZString(pMe, szLabel);
- QCBOREncode_AddBytesLenOnly(pMe, Bytes);
-}
-
-static inline void
-QCBOREncode_AddBytesLenOnlyToMapN(QCBOREncodeContext *pMe,
- const int64_t nLabel,
- const UsefulBufC Bytes)
-{
- QCBOREncode_AddInt64(pMe, nLabel);
- QCBOREncode_AddBytesLenOnly(pMe, Bytes);
-}
-
-
static inline void
QCBOREncode_AddTBinaryUUID(QCBOREncodeContext *pMe,
const uint8_t uTagRequirement,
@@ -3758,8 +3961,15 @@
static inline void
QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint8_t uNum)
{
- /* This check often is optimized out because uNum is known at compile time. */
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(pMe->uMode >= QCBOR_ENCODE_MODE_DCBOR) {
+ if(uNum < CBOR_SIMPLEV_FALSE ||
+ uNum > CBOR_SIMPLEV_NULL) {
+ pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+ return;
+ }
+ }
+ /* This check often is optimized out because uNum is known at compile time. */
if(uNum >= CBOR_SIMPLEV_RESERVED_START && uNum <= CBOR_SIMPLEV_RESERVED_END) {
pMe->uError = QCBOR_ERR_ENCODE_UNSUPPORTED;
return;
@@ -3875,6 +4085,7 @@
QCBOREncode_OpenArray(pMe);
}
+
static inline void
QCBOREncode_CloseArray(QCBOREncodeContext *pMe)
{
@@ -3905,7 +4116,7 @@
static inline void
QCBOREncode_CloseMap(QCBOREncodeContext *pMe)
{
- QCBOREncode_Private_CloseMapOrArray(pMe, CBOR_MAJOR_TYPE_MAP);
+ (pMe->pfnCloseMap)(pMe);
}
static inline void
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index a061809..04d0d11 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -92,9 +92,9 @@
* should return 'x'.
* FLOAT_ERR_CODE_NO_HALF_PREC_NO_FLOAT_HW(x) Can be used when either disabled
* preferred float or disabling
- * hardware floating point results in
- * error, and all other cases should
- * return 'x'.
+ * hardware floating point results
+ * in error, and all other cases
+ * should return 'x'.
*/
#ifdef USEFULBUF_DISABLE_ALL_FLOAT
#define FLOAT_ERR_CODE_NO_FLOAT(x) QCBOR_ERR_ALL_FLOAT_DISABLED
@@ -165,6 +165,8 @@
* a uin32_t.
*
* This will cause trouble on a machine where size_t is less than 32-bits.
+ *
+ * TODO: make this public?
*/
#define QCBOR_MAX_ARRAY_OFFSET (UINT32_MAX - 100)
@@ -215,14 +217,29 @@
* functions to form a public "object" that does the job of encdoing.
*
* Size approximation (varies with CPU/compiler):
- * 64-bit machine: 27 + 1 (+ 4 padding) + 136 = 32 + 136 = 168 bytes
+ * 64-bit machine: 27 + 1 (+ 4 padding) + 136 = 32 + 136 = 168 bytes
* 32-bit machine: 15 + 1 + 132 = 148 bytes
*/
+typedef struct _QCBOREncodeContext QCBORPrivateEncodeContext;
+
+
+/* These are in order of increasing strictness. Order is relied upon in
+ * implementation. */
+#define QCBOR_ENCODE_MODE_ANY 0
+#define QCBOR_ENCODE_MODE_PREFERRED 1
+#define QCBOR_ENCODE_MODE_CDE 2
+#define QCBOR_ENCODE_MODE_DCBOR 3
+
+
struct _QCBOREncodeContext {
/* 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 uAllow; /* @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD, ... */
+ void (*pfnCloseMap)(QCBORPrivateEncodeContext *); /* Use of function
+ * pointer explained in QCBOREncode_SerializationCDE() */
QCBORTrackNesting nesting; /* Keep track of array and map nesting */
};
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index 9e0cd07..d1e5d20 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -297,6 +297,54 @@
int64_t *pnValue);
+#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+/**
+ * @brief Decode next as a number with precision-preserving conversions.
+ *
+ * @param[in] pCtx The decode context.
+ * @param[out] pNumber The returned 64-bit signed integer.
+ *
+ * This will get the next item as a number and return 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.
+ *
+ * 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
+ * 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.
+ *
+ * All others are returned as a double.
+ *
+ * This is useful for dCBOR which essentially combines floats
+ * and integers into one number space.
+ *
+ * Please see @ref Decode-Errors-Overview "Decode Errors Overview".
+ *
+ * See also QCBORDecode_GetNumberConvertPreciselyBig().
+ */
+void
+QCBORDecode_GetNumberConvertPrecisely(QCBORDecodeContext *pCtx,
+ QCBORItem *pNumber);
+
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
/**
* @brief Decode next item into a signed 64-bit integer with conversions.
*
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index f90790b..91ffda1 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -1,4 +1,4 @@
-/* ==========================================================================
+/*==============================================================================
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2024, Laurence Lundblade.
* All rights reserved.
@@ -45,6 +45,10 @@
when who what, where, why
-------- ---- ---------------------------------------------------
21/05/2024 llundblade Comment formatting and some code tidiness.
+ 28/02/2022 llundblade Rearrange UsefulOutBuf_Compare().
+ 19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
+ 19/11/2023 llundblade Add UsefulOutBuf_Swap().
+ 19/11/2023 llundblade Add UsefulOutBuf_Compare().
19/12/2022 llundblade Don't pass NULL to memmove when adding empty data.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf
3/6/2021 mcr/llundblade Fix warnings related to --Wcast-qual
@@ -441,3 +445,123 @@
pMe->cursor += uAmount;
return result;
}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+int UsefulOutBuf_Compare(UsefulOutBuf *pMe,
+ const size_t uStart1, const size_t uLen1,
+ const size_t uStart2, const size_t uLen2)
+{
+ const uint8_t *pBase;
+ const uint8_t *pEnd;
+ const uint8_t *p1;
+ const uint8_t *p2;
+ const uint8_t *p1End;
+ const uint8_t *p2End;
+ int uComparison;
+
+ pBase = pMe->UB.ptr;
+ pEnd = (const uint8_t *)pBase + pMe->data_len;
+ p1 = pBase + uStart1;
+ p2 = pBase + uStart2;
+ p1End = p1 + uLen1;
+ p2End = p2 + uLen2;
+
+ uComparison = 0;
+ while(p1 < pEnd && p2 < pEnd && p1 < p1End && p2 < p2End) {
+ uComparison = *p2 - *p1;
+ if(uComparison != 0) {
+ break;;
+ }
+ p1++;
+ p2++;
+ }
+
+ if(uComparison == 0 && p1 != p1End && p2 != p2End) {
+ if(uLen1 > uLen2) {
+ uComparison = 1;
+ } else if(uLen2 < uLen1){
+ uComparison = -1;
+ } else {
+ return 0;
+ }
+ }
+
+ return uComparison;
+}
+
+
+
+/**
+ * @brief Reverse order of bytes in a buffer.
+ *
+ * This reverses bytes starting at pStart, up to, but not including
+ * the byte at pEnd
+ */
+static void
+UsefulOutBuf_Private_ReverseBytes(uint8_t *pStart, uint8_t *pEnd)
+{
+ uint8_t uTmp;
+
+ while(pStart < pEnd) {
+ pEnd--;
+ uTmp = *pStart;
+ *pStart = *pEnd;
+ *pEnd = uTmp;
+ pStart++;
+ }
+}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+void UsefulOutBuf_Swap(UsefulOutBuf *pMe, size_t uStartOffset, size_t uPivotOffset, size_t uEndOffset)
+{
+ uint8_t *pBase;
+
+ if(uStartOffset > pMe->data_len || uPivotOffset > pMe->data_len || uEndOffset > pMe->data_len) {
+ return;
+ }
+
+ if(uStartOffset > uPivotOffset || uStartOffset > uEndOffset || uPivotOffset > uEndOffset) {
+ return;
+ }
+
+ /* This is the "reverse" algorithm to swap two memory regions */
+ pBase = pMe->UB.ptr;
+ UsefulOutBuf_Private_ReverseBytes(pBase + uStartOffset, pBase + uPivotOffset);
+ UsefulOutBuf_Private_ReverseBytes(pBase + uPivotOffset, pBase + uEndOffset);
+ UsefulOutBuf_Private_ReverseBytes(pBase + uStartOffset, pBase + uEndOffset);
+}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ */
+UsefulBufC
+UsefulOutBuf_OutUBufOffset(UsefulOutBuf *pMe, size_t uOffset)
+{
+ UsefulBufC ReturnValue;
+
+ ReturnValue = UsefulOutBuf_OutUBuf(pMe);
+
+ if(UsefulBuf_IsNULLC(ReturnValue)) {
+ return NULLUsefulBufC;
+ }
+
+ if(uOffset >= ReturnValue.len) {
+ return NULLUsefulBufC;
+ }
+
+ ReturnValue.ptr = (const uint8_t *)ReturnValue.ptr + uOffset;
+ ReturnValue.len -= uOffset;
+
+ return ReturnValue;
+}
diff --git a/src/ieee754.c b/src/ieee754.c
index 2d98159..69bf113 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -1,5 +1,5 @@
/* ==========================================================================
- * ieee754.c -- floating-point conversion between half, double & single-precision
+ * ieee754.c -- floating-point conversion for half, double & single-precision
*
* Copyright (c) 2018-2024, Laurence Lundblade. All rights reserved.
* Copyright (c) 2021, Arm Limited. All rights reserved.
@@ -11,20 +11,14 @@
* Created on 7/23/18
* ========================================================================== */
-/*
- * Include before QCBOR_DISABLE_PREFERRED_FLOAT is checked as
- * QCBOR_DISABLE_PREFERRED_FLOAT might be defined in qcbor/qcbor_common.h
- */
#include "qcbor/qcbor_common.h"
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
-
#include "ieee754.h"
#include <string.h> /* For memcpy() */
/*
- * This code has long lines and is easier to read because of
+ * This has long lines and is easier to read because of
* them. Some coding guidelines prefer 80 column lines (can they not
* afford big displays?).
*
@@ -164,6 +158,10 @@
return u64;
}
+
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+
+
static inline double
CopyUint64ToDouble(uint64_t u64)
{
@@ -184,7 +182,7 @@
/**
- * @brief Assemble sign, significand and exponent into single precision float.
+ * @brief Assemble sign, significand and exponent into double precision float.
*
* @param[in] uDoubleSign 0 if positive, 1 if negative
* @pararm[in] uDoubleSignificand Bits of the significand
@@ -193,6 +191,7 @@
* This returns the bits for a single-precision float, a binary64
* as specified in IEEE754.
*/
+// TODO: make the sign and exponent type int?
static double
IEEE754_AssembleDouble(uint64_t uDoubleSign,
uint64_t uDoubleSignificand,
@@ -208,6 +207,7 @@
}
+/* Public function; see ieee754.h */
double
IEEE754_HalfToDouble(uint16_t uHalfPrecision)
{
@@ -315,7 +315,7 @@
/* Public function; see ieee754.h */
IEEE754_union
-IEEE754_SingleToHalf(float f)
+IEEE754_SingleToHalf(const float f, const int bNoNaNPayload)
{
IEEE754_union result;
uint32_t uDroppedBits;
@@ -357,28 +357,36 @@
result.uSize = IEEE754_UNION_IS_HALF;
result.uValue = IEEE754_AssembleHalf(uSingleSign, 0, HALF_EXPONENT_INF_OR_NAN);
} else {
- /* The NaN can only be converted if no payload bits are lost
- * per RFC 8949 section 4.1 that defines Preferred
- * Serializaton. Note that Deterministically Encode CBOR in
- * section 4.2 allows for some variation of this rule, but at
- * the moment this implementation is of Preferred
- * Serialization, not CDE. As of December 2023, we are also
- * expecting an update to CDE. This code may need to be
- * updated for CDE.
- */
- uDroppedBits = uSingleSignificand & (SINGLE_SIGNIFICAND_MASK >> HALF_NUM_SIGNIFICAND_BITS);
- if(uDroppedBits == 0) {
- /* --- IS CONVERTABLE NAN --- */
- uHalfSignificand = uSingleSignificand >> (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ if(bNoNaNPayload) {
+ /* --- REQUIRE CANNONICAL NAN --- */
result.uSize = IEEE754_UNION_IS_HALF;
result.uValue = IEEE754_AssembleHalf(uSingleSign,
- uHalfSignificand,
+ HALF_QUIET_NAN_BIT,
HALF_EXPONENT_INF_OR_NAN);
-
} else {
- /* --- IS UNCONVERTABLE NAN --- */
- result.uSize = IEEE754_UNION_IS_SINGLE;
- result.uValue = uSingle;
+ /* The NaN can only be converted if no payload bits are lost
+ * per RFC 8949 section 4.1 that defines Preferred
+ * Serializaton. Note that Deterministically Encode CBOR in
+ * section 4.2 allows for some variation of this rule, but at
+ * the moment this implementation is of Preferred
+ * Serialization, not CDE. As of December 2023, we are also
+ * expecting an update to CDE. This code may need to be
+ * updated for CDE.
+ */
+ uDroppedBits = uSingleSignificand & (SINGLE_SIGNIFICAND_MASK >> HALF_NUM_SIGNIFICAND_BITS);
+ if(uDroppedBits == 0) {
+ /* --- IS CONVERTABLE NAN --- */
+ uHalfSignificand = uSingleSignificand >> (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+ result.uSize = IEEE754_UNION_IS_HALF;
+ result.uValue = IEEE754_AssembleHalf(uSingleSign,
+ uHalfSignificand,
+ HALF_EXPONENT_INF_OR_NAN);
+
+ } else {
+ /* --- IS UNCONVERTABLE NAN --- */
+ result.uSize = IEEE754_UNION_IS_SINGLE;
+ result.uValue = uSingle;
+ }
}
}
} else {
@@ -495,7 +503,7 @@
* This handles all subnormals and NaN payloads.
*/
static IEEE754_union
-IEEE754_DoubleToSingle(double d)
+IEEE754_DoubleToSingle(const double d)
{
IEEE754_union Result;
int64_t nExponentDifference;
@@ -514,7 +522,6 @@
const uint64_t uDoubleSign = (uDouble & DOUBLE_SIGN_MASK) >> DOUBLE_SIGN_SHIFT;
const uint64_t uDoubleSignificand = uDouble & DOUBLE_SIGNIFICAND_MASK;
-
if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_ZERO) {
if(uDoubleSignificand == 0) {
/* --- IS ZERO --- */
@@ -619,7 +626,9 @@
/* Public function; see ieee754.h */
IEEE754_union
-IEEE754_DoubleToSmaller(double d, int bAllowHalfPrecision)
+IEEE754_DoubleToSmaller(const double d,
+ const int bAllowHalfPrecision,
+ const int bNoNanPayload)
{
IEEE754_union result;
@@ -629,15 +638,276 @@
/* Cast to uint32_t is OK, because value was just successfully
* converted to single. */
float uSingle = CopyUint32ToSingle((uint32_t)result.uValue);
- result = IEEE754_SingleToHalf(uSingle);
+ result = IEEE754_SingleToHalf(uSingle, bNoNanPayload);
}
return result;
}
-#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
-int ieee754_dummy_place_holder;
+/* This returns 64 minus the number of zero bits on the right. It is
+ * is the amount of precision in the 64-bit significand passed in.
+ * When used for 52 and 23-bit significands, subtract 12 and 41
+ * to get their precision.
+ *
+ * The value returned is for a *normalized* number like the
+ * significand of a double. When used for precision for a non-normalized
+ * number like a uint64_t, further computation is required.
+ *
+ * If the significand is 0, then 0 is returned as the precision.*/
+static int
+IEEE754_Private_CountPrecisionBits(uint64_t uSignigicand)
+{
+ int nNonZeroBitsCount;
+ uint64_t uMask;
+
+ for(nNonZeroBitsCount = 64; nNonZeroBitsCount > 0; nNonZeroBitsCount--) {
+ uMask = 0x01UL << (64 - nNonZeroBitsCount);
+ if(uMask & uSignigicand) {
+ break;
+ }
+ }
+
+ return nNonZeroBitsCount;
+}
+
+
+
+/* Public function; see ieee754.h */
+struct IEEE754_ToInt
+IEEE754_DoubleToInt(const double d)
+{
+ int64_t nPrecisionBits;
+ struct IEEE754_ToInt Result;
+ uint64_t uInteger;
+
+ /* Pull the three parts out of the double-precision float. Most
+ * work is done with uint64_t which helps avoid integer promotions
+ * and static analyzer complaints.
+ */
+ const uint64_t uDouble = CopyDoubleToUint64(d);
+ const uint64_t uDoubleBiasedExponent = (uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT;
+ /* 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;
+
+ if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_ZERO) {
+ if(uDoubleSignificand == 0) {
+ /* --- POSITIVE AND NEGATIVE ZERO --- */
+ Result.integer.un_signed = 0;
+ Result.type = IEEE754_ToInt_IS_UINT;
+ } else {
+ /* --- SUBNORMAL --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ }
+ } else if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_INF_OR_NAN) {
+ if(uDoubleSignificand != 0) {
+ /* --- NAN --- */
+ Result.type = IEEE754_ToInt_NaN; /* dCBOR doesn't care about payload */
+ } else {
+ /* --- INIFINITY --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ }
+ } else if(nDoubleUnbiasedExponent < 0 ||
+ (nDoubleUnbiasedExponent >= ((uDouble & DOUBLE_SIGN_MASK) ? 63 : 64))) {
+ /* --- 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
+ * 64-bit integers always have more precision than the 52-bits
+ * of a double.
+ */
+ nPrecisionBits = IEEE754_Private_CountPrecisionBits(uDoubleSignificand) -
+ (64-DOUBLE_NUM_SIGNIFICAND_BITS);
+
+ if(nPrecisionBits && nPrecisionBits > nDoubleUnbiasedExponent) {
+ /* --- Not a whole number --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ } else {
+ /* --- CONVERTABLE WHOLE NUMBER --- */
+ /* Add in the one that is implied in normal floats */
+ uInteger = uDoubleSignificand + (1ULL << DOUBLE_NUM_SIGNIFICAND_BITS);
+ /* Factor in the exponent */
+ if(nDoubleUnbiasedExponent < DOUBLE_NUM_SIGNIFICAND_BITS) {
+ /* Numbers less than 2^52 with up to 52 significant bits */
+ uInteger >>= DOUBLE_NUM_SIGNIFICAND_BITS - nDoubleUnbiasedExponent;
+ } else {
+ /* Numbers greater than 2^52 with at most 52 significant bits */
+ uInteger <<= nDoubleUnbiasedExponent - DOUBLE_NUM_SIGNIFICAND_BITS;
+ }
+ if(uDouble & DOUBLE_SIGN_MASK) {
+ /* Cast safe because exponent range check above */
+ 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;
+ }
+ }
+ }
+
+ return Result;
+}
+
+
+/* Public function; see ieee754.h */
+struct IEEE754_ToInt
+IEEE754_SingleToInt(const float f)
+{
+ int32_t nPrecisionBits;
+ struct IEEE754_ToInt Result;
+ uint64_t uInteger;
+
+ /* Pull the three parts out of the single-precision float. Most
+ * work is done with uint32_t which helps avoid integer promotions
+ * and static analyzer complaints.
+ */
+ const uint32_t uSingle = CopyFloatToUint32(f);
+ const uint32_t uSingleBiasedExponent = (uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT;
+ /* Cast safe because of mask above; exponents < SINGLE_EXPONENT_MAX */
+ const int32_t nSingleUnbiasedExponent = (int32_t)uSingleBiasedExponent - SINGLE_EXPONENT_BIAS;
+ const uint32_t uSingleleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
+
+ if(nSingleUnbiasedExponent == SINGLE_EXPONENT_ZERO) {
+ if(uSingleleSignificand == 0 && !(uSingle & SINGLE_SIGN_MASK)) {
+ /* --- POSITIVE AND NEGATIVE ZERO --- */
+ Result.integer.un_signed = 0;
+ Result.type = IEEE754_ToInt_IS_UINT;
+ } else {
+ /* --- Subnormal --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ }
+ } else if(nSingleUnbiasedExponent == SINGLE_EXPONENT_INF_OR_NAN) {
+ /* --- NAN or INFINITY --- */
+ if(uSingleleSignificand != 0) {
+ Result.type = IEEE754_ToInt_NaN; /* dCBOR doesn't care about payload */
+ } else {
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ }
+ } else if(nSingleUnbiasedExponent < 0 ||
+ (nSingleUnbiasedExponent >= ((uSingle & SINGLE_SIGN_MASK) ? 63 : 64))) {
+ /* --- 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
+ * 64-bit integers always have more precision than the 23 bits
+ * of a single.
+ */
+ nPrecisionBits = IEEE754_Private_CountPrecisionBits(uSingleleSignificand) -
+ (64 - SINGLE_NUM_SIGNIFICAND_BITS);
+
+ if(nPrecisionBits && nPrecisionBits > nSingleUnbiasedExponent) {
+ /* --- Not a whole number --- */
+ Result.type = IEEE754_ToInt_NO_CONVERSION;
+ } else {
+ /* --- CONVERTABLE WHOLE NUMBER --- */
+ /* Add in the one that is implied in normal floats */
+ uInteger = uSingleleSignificand + (1ULL << SINGLE_NUM_SIGNIFICAND_BITS);
+ /* Factor in the exponent */
+ if(nSingleUnbiasedExponent < SINGLE_NUM_SIGNIFICAND_BITS) {
+ /* Numbers less than 2^23 with up to 23 significant bits */
+ uInteger >>= SINGLE_NUM_SIGNIFICAND_BITS - nSingleUnbiasedExponent;
+ } else {
+ /* 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;
+ } else {
+ Result.integer.un_signed = uInteger;
+ Result.type = IEEE754_ToInt_IS_UINT;
+ }
+ }
+ }
+
+ return Result;
+}
+
+
+
+/* Public function; see ieee754.h */
+double
+IEEE754_UintToDouble(const uint64_t uInt, const int uIsNegative)
+{
+ int nDoubleUnbiasedExponent;
+ 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;
+ }
+ 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,
+ uDoubleSignificand,
+ nDoubleUnbiasedExponent);
+}
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+
+/* Public function; see ieee754.h */
+int
+IEEE754_IsNotStandardDoubleNaN(const double d)
+{
+ const uint64_t uDouble = CopyDoubleToUint64(d);
+ const uint64_t uDoubleBiasedExponent = (uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT;
+ /* 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;
+
+ if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_INF_OR_NAN &&
+ uDoubleSignificand != 0 &&
+ uDoubleSignificand != DOUBLE_QUIET_NAN_BIT) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/* Public function; see ieee754.h */
+int
+IEEE754_IsNotStandardSingleNaN(const float f)
+{
+ const uint32_t uSingle = CopyFloatToUint32(f);
+ const uint32_t uSingleBiasedExponent = (uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT;
+ /* Cast safe because of mask above; exponents < SINGLE_EXPONENT_MAX */
+ const int32_t nSingleUnbiasedExponent = (int32_t)uSingleBiasedExponent - SINGLE_EXPONENT_BIAS;
+ const uint32_t uSingleleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
+
+ if(nSingleUnbiasedExponent == SINGLE_EXPONENT_INF_OR_NAN &&
+ uSingleleSignificand != 0 &&
+ uSingleleSignificand != SINGLE_QUIET_NAN_BIT) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/src/ieee754.h b/src/ieee754.h
index 863019b..c893e6f 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -10,11 +10,11 @@
* Created on 7/23/18
* ========================================================================== */
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
#ifndef ieee754_h
#define ieee754_h
+
#include <stdint.h>
@@ -25,6 +25,9 @@
* smaller representation (e.g., double to single) that does not lose
* precision for CBOR preferred serialization.
*
+ * This also implements conversion of floats to whole numbers as
+ * is required for dCBOR.
+ *
* This implementation works entirely with shifts and masks and does
* not require any floating-point HW or library.
*
@@ -51,6 +54,7 @@
* conversion. This version is reduced to what is needed for CBOR.
*/
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
/**
* @brief Convert half-precision float to double-precision float.
@@ -87,6 +91,22 @@
} IEEE754_union;
+/** Holds result of an attempt to convert a floating-point
+ * number to an int64_t or uint64_t.
+ */
+struct IEEE754_ToInt {
+ enum {IEEE754_ToInt_IS_INT,
+ IEEE754_ToInt_IS_UINT,
+ IEEE754_ToInt_NO_CONVERSION,
+ IEEE754_ToInt_NaN
+ } type;
+ union {
+ uint64_t un_signed;
+ int64_t is_signed;
+ } integer;
+};
+
+
/**
* @brief Convert a double to either single or half-precision.
*
@@ -102,7 +122,7 @@
* This handles all subnormals and NaN payloads.
*/
IEEE754_union
-IEEE754_DoubleToSmaller(double d, int bAllowHalfPrecision);
+IEEE754_DoubleToSmaller(double d, int bAllowHalfPrecision, int bNoNaNPayload);
/**
@@ -118,9 +138,108 @@
* This handles all subnormals and NaN payloads.
*/
IEEE754_union
-IEEE754_SingleToHalf(float f);
+IEEE754_SingleToHalf(float f, int bNoNanPayloads);
+
+
+/**
+ * @brief Convert a double-precision float to integer if whole number
+ *
+ * @param[in] d The value to convert.
+ *
+ * @returns Either converted number or conversion status.
+ *
+ * If the value is a whole number that will fit either in a uint64_t
+ * or an int64_t, it is converted. If it is a NaN, then there is no
+ * conversion and and the fact that it is a NaN is indicated in the
+ * returned structure. If it can't be converted, then that is
+ * indicated in the returned structure.
+ *
+ * This always returns postive numbers as a uint64_t even if they will
+ * fit in an int64_t.
+ *
+ * This never fails becaue of precision, but may fail because of range.
+ */
+struct IEEE754_ToInt
+IEEE754_DoubleToInt(double d);
+
+
+/**
+ * @brief Convert a single-precision float to integer if whole number
+ *
+ * @param[in] f The value to convert.
+ *
+ * @returns Either converted number or conversion status.
+ *
+ * If the value is a whole number that will fit either in a uint64_t
+ * or an int64_t, it is converted. If it is a NaN, then there is no
+ * conversion and and the fact that it is a NaN is indicated in the
+ * returned structure. If it can't be converted, then that is
+ * indicated in the returned structure.
+ *
+ * This always returns postive numbers as a uint64_t even if they will
+ * fit in an int64_t.
+ *
+ * This never fails becaue of precision, but may fail because of range.
+ */
+struct IEEE754_ToInt
+IEEE754_SingleToInt(float f);
+
+
+/**
+ * @brief Convert an unsigned integer to a double with no precision loss.
+ *
+ * @param[in] uInt The value to convert.
+ * @param[in] uIsNegative 0 if postive, 1 if negative.
+ *
+ * @returns Either the converted number or 0.5 if no conversion.
+ *
+ * The conversion will fail if the input can not be represented in the
+ * 52 bits or precision that a double has. 0.5 is returned to indicate
+ * no conversion. It is out-of-band from non-error results, because
+ * all non-error results are whole integers.
+ */
+#define IEEE754_UINT_TO_DOUBLE_OOB 0.5
+double
+IEEE754_UintToDouble(uint64_t uInt, int uIsNegative);
+
+
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+/**
+ * @brief Tests whether NaN is "quiet" vs having a payload.
+ *
+ * @param[in] dNum Double number to test.
+ *
+ * @returns 0 if a quiet NaN, 1 if it has a payload.
+ *
+ * A quiet NaN is usually represented as 0x7ff8000000000000. That is
+ * the significand bits are 0x8000000000000. If the significand bits
+ * are other than 0x8000000000000 it is considered to have a NaN
+ * payload.
+ *
+ * Note that 0x7ff8000000000000 is not specified in a standard, but it
+ * is commonly implemented and chosen by CBOR as the best way to
+ * represent a NaN.
+ */
+int
+IEEE754_IsNotStandardDoubleNaN(double dNum);
+
+
+
+/**
+ * @brief Tests whether NaN is "quiet" vs having a payload.
+ *
+ * @param[in] fNum Float number to test.
+ *
+ * @returns 0 if a quiet NaN, 1 if it has a payload.
+ *
+ * See IEEE754_IsNotStandardDoubleNaN(). A single precision quiet NaN
+ * is 0x7fc00000.
+ */
+int
+IEEE754_IsNotStandardSingleNaN(float fNum);
#endif /* ieee754_h */
-#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 3ed4d0f..13a728d 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -904,10 +904,8 @@
pDecodedItem->uDataType = QCBOR_TYPE_INT64;
} else {
- /* C can't represent a negative integer in this range so it
- * is an error.
- */
- uReturn = QCBOR_ERR_INT_OVERFLOW;
+ pDecodedItem->val.uint64 = uArgument;
+ pDecodedItem->uDataType = QCBOR_TYPE_65BIT_NEG_INT;
}
}
@@ -5555,6 +5553,12 @@
}
break;
+ case QCBOR_TYPE_65BIT_NEG_INT:
+ /* This type occurs if the value won't fit into int64_t
+ * so this is always an error. */
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ break;
+
default:
return QCBOR_ERR_UNEXPECTED_TYPE;
}
@@ -5963,12 +5967,15 @@
case QCBOR_TYPE_UINT64:
if(uConvertTypes & QCBOR_CONVERT_TYPE_XINT64) {
- *puValue = pItem->val.uint64;
+ *puValue = pItem->val.uint64;
} else {
return QCBOR_ERR_UNEXPECTED_TYPE;
}
break;
+ case QCBOR_TYPE_65BIT_NEG_INT:
+ return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+
default:
return QCBOR_ERR_UNEXPECTED_TYPE;
}
@@ -6363,6 +6370,14 @@
return QCBOR_ERR_HW_FLOAT_DISABLED;
#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+ case QCBOR_TYPE_65BIT_NEG_INT:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ *pdValue = -(double)pItem->val.uint64 - 1;
+ break;
+#else
+ return QCBOR_ERR_HW_FLOAT_DISABLED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+
default:
return QCBOR_ERR_UNEXPECTED_TYPE;
}
@@ -7433,3 +7448,92 @@
}
#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
+
+
+#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+/*
+ * Public function, see header qcbor/qcbor_spiffy_decode.h file
+ */
+void
+QCBORDecode_GetNumberConvertPrecisely(QCBORDecodeContext *pMe,
+ QCBORItem *pNumber)
+{
+ QCBORItem Item;
+ struct IEEE754_ToInt ToInt;
+ double d;
+ QCBORError uError;
+
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ uError = QCBORDecode_GetNext(pMe, &Item);
+ if(uError != QCBOR_SUCCESS) {
+ pMe->uLastError = (uint8_t)uError;
+ return;
+ }
+
+ switch(Item.uDataType) {
+ case QCBOR_TYPE_INT64:
+ case QCBOR_TYPE_UINT64:
+ *pNumber = Item;
+ break;
+
+ case QCBOR_TYPE_DOUBLE:
+ ToInt = IEEE754_DoubleToInt(Item.val.dfnum);
+ if(ToInt.type == IEEE754_ToInt_IS_INT) {
+ pNumber->uDataType = QCBOR_TYPE_INT64;
+ pNumber->val.int64 = ToInt.integer.is_signed;
+ } else if(ToInt.type == IEEE754_ToInt_IS_UINT) {
+ if(ToInt.integer.un_signed <= INT64_MAX) {
+ /* Do the same as base QCBOR integer decoding */
+ pNumber->uDataType = QCBOR_TYPE_INT64;
+ pNumber->val.int64 = (int64_t)ToInt.integer.un_signed;
+ } else {
+ pNumber->uDataType = QCBOR_TYPE_UINT64;
+ pNumber->val.uint64 = ToInt.integer.un_signed;
+ }
+ } else {
+ *pNumber = Item;
+ }
+ break;
+
+ case QCBOR_TYPE_FLOAT:
+ ToInt = IEEE754_SingleToInt(Item.val.fnum);
+ if(ToInt.type == IEEE754_ToInt_IS_INT) {
+ pNumber->uDataType = QCBOR_TYPE_INT64;
+ pNumber->val.int64 = ToInt.integer.is_signed;
+ } else if(ToInt.type == IEEE754_ToInt_IS_UINT) {
+ if(ToInt.integer.un_signed <= INT64_MAX) {
+ /* Do the same as base QCBOR integer decoding */
+ pNumber->uDataType = QCBOR_TYPE_INT64;
+ pNumber->val.int64 = (int64_t)ToInt.integer.un_signed;
+ } else {
+ pNumber->uDataType = QCBOR_TYPE_UINT64;
+ pNumber->val.uint64 = ToInt.integer.un_signed;
+ }
+ } else {
+ *pNumber = Item;
+ }
+ break;
+
+
+ case QCBOR_TYPE_65BIT_NEG_INT:
+ d = IEEE754_UintToDouble(Item.val.uint64, 1);
+ if(d == IEEE754_UINT_TO_DOUBLE_OOB) {
+ *pNumber = Item;
+ } else {
+ pNumber->uDataType = QCBOR_TYPE_DOUBLE;
+ /* -1 is because of CBOR offset of negative numbers */
+ pNumber->val.dfnum = d - 1;
+ }
+ break;
+
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ pNumber->uDataType = QCBOR_TYPE_NONE;
+ break;
+ }
+}
+
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 2a99110..d196149 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -35,6 +35,10 @@
#include "qcbor/qcbor_encode.h"
#include "ieee754.h"
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+#include <math.h> /* Only for NAN definition */
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
/**
* @file qcbor_encode.c
@@ -248,6 +252,11 @@
*/
+/* Forward declaration for reference in QCBOREncode_Init() */
+static void
+QCBOREncode_Private_CloseMapUnsorted(QCBOREncodeContext *pMe);
+
+
/*
* Public function for initialization. See qcbor/qcbor_encode.h
*/
@@ -257,6 +266,7 @@
memset(pMe, 0, sizeof(QCBOREncodeContext));
UsefulOutBuf_Init(&(pMe->OutBuf), Storage);
Nesting_Init(&(pMe->nesting));
+ pMe->pfnCloseMap = QCBOREncode_Private_CloseMapUnsorted;
}
@@ -529,7 +539,7 @@
* @param pMe Encoder context.
* @param uMajorType Major type to insert.
* @param uArgument The argument (an integer value or a length).
- * @param uMinLen The minimum number of bytes for encoding the CBOR argument.
+ * @param uMinLen Minimum number of bytes for encoding the CBOR argument.
*
* This formats the CBOR "head" and appends it to the output.
*
@@ -570,6 +580,29 @@
/*
+ * 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
@@ -581,9 +614,9 @@
if(nNum < 0) {
/* In CBOR -1 encodes as 0x00 with major type negative int.
* First add one as a signed integer because that will not
- * overflow. Then change the sign as needed for encoding. (The
+ * overflow. Then change the sign as needed for encoding (the
* opposite order, changing the sign and subtracting, can cause
- * an overflow when encoding INT64_MIN. */
+ * an overflow when encoding INT64_MIN). */
int64_t nTmp = nNum + 1;
uValue = (uint64_t)-nTmp;
uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
@@ -641,10 +674,42 @@
* without a loss of precision. See QCBOREncode_AddDouble().
*/
void
-QCBOREncode_Private_AddPreferredDouble(QCBOREncodeContext *pMe, const double dNum)
+QCBOREncode_Private_AddPreferredDouble(QCBOREncodeContext *pMe, double dNum)
{
- const IEEE754_union uNum = IEEE754_DoubleToSmaller(dNum, true);
- QCBOREncode_Private_AddType7(pMe, (uint8_t)uNum.uSize, uNum.uValue);
+ IEEE754_union FloatResult;
+ bool bNoNaNPayload;
+ struct IEEE754_ToInt IntResult;
+
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(IEEE754_IsNotStandardDoubleNaN(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+ pMe->uError = QCBOR_ERR_NOT_ALLOWED;
+ return;
+ }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+ if(pMe->uMode == QCBOR_ENCODE_MODE_DCBOR) {
+ IntResult = IEEE754_DoubleToInt(dNum);
+ switch(IntResult.type) {
+ case IEEE754_ToInt_IS_INT:
+ QCBOREncode_AddInt64(pMe, IntResult.integer.is_signed);
+ return;
+ case IEEE754_ToInt_IS_UINT:
+ QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
+ return;
+ case IEEE754_ToInt_NaN:
+ dNum = NAN;
+ bNoNaNPayload = true;
+ break;
+ case IEEE754_ToInt_NO_CONVERSION:
+ bNoNaNPayload = true;
+ }
+ } else {
+ bNoNaNPayload = false;
+ }
+
+ FloatResult = IEEE754_DoubleToSmaller(dNum, true, bNoNaNPayload);
+
+ QCBOREncode_Private_AddType7(pMe, (uint8_t)FloatResult.uSize, FloatResult.uValue);
}
@@ -658,14 +723,47 @@
* without a loss of precision. See QCBOREncode_AddFloat().
*/
void
-QCBOREncode_Private_AddPreferredFloat(QCBOREncodeContext *pMe, const float fNum)
+QCBOREncode_Private_AddPreferredFloat(QCBOREncodeContext *pMe, float fNum)
{
- const IEEE754_union uNum = IEEE754_SingleToHalf(fNum);
- QCBOREncode_Private_AddType7(pMe, (uint8_t)uNum.uSize, uNum.uValue);
+ IEEE754_union FloatResult;
+ bool bNoNaNPayload;
+ struct IEEE754_ToInt IntResult;
+
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(IEEE754_IsNotStandardSingleNaN(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+ pMe->uError = QCBOR_ERR_NOT_ALLOWED;
+ return;
+ }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+ if(pMe->uMode == QCBOR_ENCODE_MODE_DCBOR) {
+ IntResult = IEEE754_SingleToInt(fNum);
+ switch(IntResult.type) {
+ case IEEE754_ToInt_IS_INT:
+ QCBOREncode_AddInt64(pMe, IntResult.integer.is_signed);
+ return;
+ case IEEE754_ToInt_IS_UINT:
+ QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
+ return;
+ case IEEE754_ToInt_NaN:
+ fNum = NAN;
+ bNoNaNPayload = true;
+ break;
+ case IEEE754_ToInt_NO_CONVERSION:
+ bNoNaNPayload = true;
+ }
+ } else {
+ bNoNaNPayload = false;
+ }
+
+ FloatResult = IEEE754_SingleToHalf(fNum, bNoNaNPayload);
+
+ QCBOREncode_Private_AddType7(pMe, (uint8_t)FloatResult.uSize, FloatResult.uValue);
}
#endif /* !QCBOR_DISABLE_PREFERRED_FLOAT */
+
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/**
* @brief Semi-private method to add bigfloats and decimal fractions.
@@ -788,6 +886,12 @@
QCBOREncode_Private_OpenMapOrArrayIndefiniteLength(QCBOREncodeContext *pMe,
const uint8_t uMajorType)
{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(pMe->uMode >= QCBOR_ENCODE_MODE_PREFERRED) {
+ pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+ return;
+ }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
/* Insert the indefinite length marker (0x9f for arrays, 0xbf for maps) */
QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, 0, 0);
@@ -895,12 +999,10 @@
/**
- * @brief Semi-private method to close a map, array or bstr wrapped CBOR
+ * @brief Semi-private method to close a map, array or bstr wrapped CBOR.
*
* @param[in] pMe The context to add to.
* @param[in] uMajorType The major CBOR type to close.
- *
- * Call QCBOREncode_CloseArray() or QCBOREncode_CloseMap() instead of this.
*/
void
QCBOREncode_Private_CloseMapOrArray(QCBOREncodeContext *pMe,
@@ -910,6 +1012,339 @@
}
+/**
+ * @brief Private method to close a map without sorting.
+ *
+ * @param[in] pMe The encode context with map to close.
+ *
+ * See QCBOREncode_SerializationCDE() implemention for explantion for why
+ * this exists in this form.
+ */
+static void
+QCBOREncode_Private_CloseMapUnsorted(QCBOREncodeContext *pMe)
+{
+ QCBOREncode_Private_CloseMapOrArray(pMe, CBOR_MAJOR_TYPE_MAP);
+}
+
+
+/**
+ * @brief Decode a CBOR item head.
+ *
+ * @param[in] pUInBuf UsefulInputBuf to read from.
+ * @param[out] pnMajorType Major type of decoded head.
+ * @param[out] puArgument Argument of decoded head.
+ * @param[out] pnAdditionalInfo Additional info from decoded head.
+ *
+ * @return SUCCESS if a head was decoded
+ * HIT_END if there were not enough bytes to decode a head
+ * UNSUPPORTED if the decoded item is not one that is supported
+ *
+ * This is copied from qcbor_decode.c rather than referenced. This
+ * makes the core decoder 60 bytes smaller because it gets inlined.
+ * It would not get inlined if it was referenced. It is important to
+ * make the core decoder as small as possible. The copy here does make
+ * map sorting 200 bytes bigger, but map sorting is rarely used in
+ * environments that need small object code. It would also make
+ * qcbor_encode.c depend on qcbor_decode.c
+ *
+ * This is also super stable and tested. It implements the very
+ * well-defined part of CBOR that will never change. So this won't
+ * change.
+ */
+static QCBORError
+QCBOREncodePriv_DecodeHead(UsefulInputBuf *pUInBuf,
+ int *pnMajorType,
+ uint64_t *puArgument,
+ int *pnAdditionalInfo)
+{
+ QCBORError uReturn;
+
+ /* Get the initial byte that every CBOR data item has and break it
+ * down. */
+ const int nInitialByte = (int)UsefulInputBuf_GetByte(pUInBuf);
+ const int nTmpMajorType = nInitialByte >> 5;
+ const int nAdditionalInfo = nInitialByte & 0x1f;
+
+ /* Where the argument accumulates */
+ uint64_t uArgument;
+
+ if(nAdditionalInfo >= LEN_IS_ONE_BYTE && nAdditionalInfo <= LEN_IS_EIGHT_BYTES) {
+ /* Need to get 1,2,4 or 8 additional argument bytes. Map
+ * LEN_IS_ONE_BYTE..LEN_IS_EIGHT_BYTES to actual length.
+ */
+ static const uint8_t aIterate[] = {1,2,4,8};
+
+ /* Loop getting all the bytes in the argument */
+ uArgument = 0;
+ for(int i = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE]; i; i--) {
+ /* This shift and add gives the endian conversion. */
+ uArgument = (uArgument << 8) + UsefulInputBuf_GetByte(pUInBuf);
+ }
+ } else if(nAdditionalInfo >= ADDINFO_RESERVED1 && nAdditionalInfo <= ADDINFO_RESERVED3) {
+ /* The reserved and thus-far unused additional info values */
+ uReturn = QCBOR_ERR_UNSUPPORTED;
+ goto Done;
+ } else {
+ /* Less than 24, additional info is argument or 31, an
+ * indefinite-length. No more bytes to get.
+ */
+ uArgument = (uint64_t)nAdditionalInfo;
+ }
+
+ if(UsefulInputBuf_GetError(pUInBuf)) {
+ uReturn = QCBOR_ERR_HIT_END;
+ goto Done;
+ }
+
+ /* All successful if arrived here. */
+ uReturn = QCBOR_SUCCESS;
+ *pnMajorType = nTmpMajorType;
+ *puArgument = uArgument;
+ *pnAdditionalInfo = nAdditionalInfo;
+
+Done:
+ return uReturn;
+}
+
+
+/**
+ * @brief Consume the next item from a UsefulInputBuf.
+ *
+ * @param[in] pInBuf UsefulInputBuf from which to consume item.
+ *
+ * Recursive, but stack usage is light and encoding depth limit
+ */
+static QCBORError
+QCBOR_Private_ConsumeNext(UsefulInputBuf *pInBuf)
+{
+ int nMajor;
+ uint64_t uArgument;
+ int nAdditional;
+ uint16_t uItemCount;
+ uint16_t uMul;
+ uint16_t i;
+ QCBORError uCBORError;
+
+ uCBORError = QCBOREncodePriv_DecodeHead(pInBuf, &nMajor, &uArgument, &nAdditional);
+ if(uCBORError != QCBOR_SUCCESS) {
+ return uCBORError;
+ }
+
+ uMul = 1;
+
+ switch(nMajor) {
+ case CBOR_MAJOR_TYPE_POSITIVE_INT: /* Major type 0 */
+ case CBOR_MAJOR_TYPE_NEGATIVE_INT: /* Major type 1 */
+ break;
+
+ case CBOR_MAJOR_TYPE_SIMPLE:
+ return uArgument == CBOR_SIMPLE_BREAK ? 1 : 0;
+ break;
+
+ case CBOR_MAJOR_TYPE_BYTE_STRING:
+ case CBOR_MAJOR_TYPE_TEXT_STRING:
+ if(nAdditional == LEN_IS_INDEFINITE) {
+ /* Segments of indefinite length */
+ while(QCBOR_Private_ConsumeNext(pInBuf) == 0);
+ }
+ (void)UsefulInputBuf_GetBytes(pInBuf, uArgument);
+ break;
+
+ case CBOR_MAJOR_TYPE_TAG:
+ QCBOR_Private_ConsumeNext(pInBuf);
+ break;
+
+ case CBOR_MAJOR_TYPE_MAP:
+ uMul = 2;
+ /* Fallthrough */
+ case CBOR_MAJOR_TYPE_ARRAY:
+ uItemCount = (uint16_t)uArgument * uMul;
+ if(nAdditional == LEN_IS_INDEFINITE) {
+ uItemCount = UINT16_MAX;
+ }
+ for(i = uItemCount; i > 0; i--) {
+ if(QCBOR_Private_ConsumeNext(pInBuf)) {
+ /* End of indefinite length */
+ break;
+ }
+ }
+ break;
+ }
+
+ return QCBOR_SUCCESS;
+}
+
+
+/**
+ * @brief Decoded next item to get its lengths.
+ *
+ * Decode the next item in map no matter what type it is. It works
+ * recursively when an item is a map or array It returns offset just
+ * past the item decoded or zero there are no more items in the output
+ * buffer.
+ *
+ * This doesn't distinguish between end of the input and an error
+ * because it is used to decode stuff we encoded into a buffer, not
+ * stuff that came in from outside. We still want a check for safety
+ * in case of bugs here, but it is OK to report end of input on error.
+ */
+struct ItemLens {
+ uint32_t uLabelLen;
+ uint32_t uItemLen;
+};
+
+static struct ItemLens
+QCBOREncode_Private_DecodeNextInMap(QCBOREncodeContext *pMe, uint32_t uStart)
+{
+ UsefulInputBuf InBuf;
+ UsefulBufC EncodedMapBytes;
+ QCBORError uCBORError;
+ struct ItemLens Result;
+
+ Result.uLabelLen = 0;
+ Result.uItemLen = 0;
+
+ EncodedMapBytes = UsefulOutBuf_OutUBufOffset(&(pMe->OutBuf), uStart);
+ if(UsefulBuf_IsNULLC(EncodedMapBytes)) {
+ return Result;
+ }
+
+ UsefulInputBuf_Init(&InBuf, EncodedMapBytes);
+
+ /* This is always used on maps, so consume two, the label and the value */
+ uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
+ if(uCBORError) {
+ return Result;
+ }
+
+ /* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
+ Result.uLabelLen = (uint32_t)UsefulInputBuf_Tell(&InBuf);
+
+ uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
+ if(uCBORError) {
+ Result.uLabelLen = 0;
+ return Result;
+ }
+
+ Result.uItemLen = (uint32_t)UsefulInputBuf_Tell(&InBuf);
+
+ /* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
+ return Result;
+}
+
+
+/**
+ * @brief Sort items lexographically by encoded labels.
+ *
+ * @param[in] pMe Encoding context.
+ * @param[in] uStart Offset in outbuf of first item for sorting.
+ *
+ * This reaches into the UsefulOutBuf in the encoding context and
+ * sorts encoded CBOR items. The byte offset start of the items is at
+ * @c uStart and it goes to the end of valid bytes in the
+ * UsefulOutBuf.
+ */
+static void
+QCBOREncode_Private_SortMap(QCBOREncodeContext *pMe, uint32_t uStart)
+{
+ bool bSwapped;
+ int nComparison;
+ uint32_t uStart1;
+ uint32_t uStart2;
+ struct ItemLens Lens1;
+ struct ItemLens Lens2;
+
+
+ if(pMe->uError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ /* Bubble sort because the sizes of all the items are not the
+ * same. It works with adjacent pairs so the swap is not too
+ * difficult even though sizes are different.
+ *
+ * While bubble sort is n-squared, it seems OK here because n will
+ * usually be small and the comparison and swap functions aren't
+ * too CPU intensive.
+ *
+ * Another approach would be to have an array of offsets to the
+ * items. However this requires memory allocation and the swap
+ * operation for quick sort or such is complicated because the item
+ * sizes are not the same and overlap may occur in the bytes being
+ * swapped.
+ */
+ do { /* Loop until nothing was swapped */
+ Lens1 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart);
+ if(Lens1.uLabelLen == 0) {
+ /* It's an empty map. Nothing to do. */
+ break;
+ }
+ uStart1 = uStart;
+ uStart2 = uStart1 + Lens1.uItemLen;
+ bSwapped = false;
+
+ while(1) {
+ Lens2 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart2);
+ if(Lens2.uLabelLen == 0) {
+ break;
+ }
+
+ nComparison = UsefulOutBuf_Compare(&(pMe->OutBuf),
+ uStart1, Lens1.uLabelLen,
+ uStart2, Lens2.uLabelLen);
+ if(nComparison < 0) {
+ UsefulOutBuf_Swap(&(pMe->OutBuf), uStart1, uStart2, uStart2 + Lens2.uItemLen);
+ uStart1 = uStart1 + Lens2.uItemLen; /* item 2 now in position of item 1 */
+ /* Lens1 is still valid as Lens1 for the next loop */
+ bSwapped = true;
+ } else if(nComparison > 0) {
+ uStart1 = uStart2;
+ Lens1 = Lens2;
+ } else /* nComparison == 0 */ {
+ pMe->uError = QCBOR_ERR_DUPLICATE_LABEL;
+ return;
+ }
+ uStart2 = uStart2 + Lens2.uItemLen;
+ }
+ } while(bSwapped);
+}
+
+
+/*
+ * Public functions for closing sorted maps. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pMe)
+{
+ uint32_t uStart;
+
+ /* The Header for the map we are about to sort hasn't been
+ * inserted yet, so uStart is the position of the first item
+ * and the end out the UsefulOutBuf data is the end of the
+ * items we are about to sort.
+ */
+ uStart = Nesting_GetStartPos(&(pMe->nesting));
+ QCBOREncode_Private_SortMap(pMe, uStart);
+
+ QCBOREncode_Private_CloseAggregate(pMe, CBOR_MAJOR_TYPE_MAP, Nesting_GetCount(&(pMe->nesting)));
+}
+
+
+/*
+ * Public functions for closing sorted maps. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pMe)
+{
+ uint32_t uStart;
+
+ uStart = Nesting_GetStartPos(&(pMe->nesting));
+ QCBOREncode_Private_SortMap(pMe, uStart);
+
+ QCBOREncode_Private_CloseMapOrArrayIndefiniteLength(pMe, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
+}
+
+
/*
* Public functions for closing bstr wrapping. See qcbor/qcbor_encode.h
*/
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index 6d2b5a4..0235466 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -989,3 +989,205 @@
return NULL;
}
+
+
+const char * UOBExtraTests(void)
+{
+ #define COMPARE_TEST_SIZE 10
+ UsefulOutBuf_MakeOnStack( UOB, COMPARE_TEST_SIZE);
+ int nCompare;
+ UsefulBufC Out;
+
+ /* Test UsefulOutBuf_Compare() */
+ UsefulOutBuf_AppendString(&UOB, "abcabdefab");
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 2, 8, 2);
+ if(nCompare != 0) {
+ return "ab should compare equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 3, 3, 3);
+ if(nCompare != 'd' - 'c') {
+ return "abc should not equal abd";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 3, 2, 8, 2);
+ if(nCompare != 0) {
+ return "ab should compare equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 2, 4, 5, 4);
+ if(nCompare != 'd' - 'c') {
+ return "ca should not equal de";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 5, 1, 2, 1);
+ if(nCompare != 'c' - 'd') {
+ return "de should not equal ca";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 7, 2, 8, 2);
+ if(nCompare != 'a' - 'f') {
+ return "fa should not equal ab";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 10, 0, 10);
+ if(nCompare != 0) {
+ return "comparison to self failed";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 9, 1, 9, 1);
+ if(nCompare != 0) {
+ return "b should compare equal to b";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 10, 1, 10, 1);
+ if(nCompare != 0) {
+ return "Comparison off the end is equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 1, 100, 1);
+ if(nCompare != 0) {
+ return "Comparison off the end is equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 100, 1, 0, 1);
+ if(nCompare != 0) {
+ return "Comparison off the end is equal";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 3, 3, 2);
+ if(nCompare > 0) {
+ return "Comparison of unequal lengths incorrect";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 8, 2, 0, 3);
+ if(nCompare < 0) {
+ return "Comparison of unequal lengths incorrect 2";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 0, 2, 2, 3);
+ if(nCompare != 'c' - 'a') {
+ return "Inequal lengths, but inequal strings";
+ }
+
+ nCompare = UsefulOutBuf_Compare(&UOB, 1, 3, 4, 2);
+ if(nCompare != 'd' - 'c') {
+ return "Inequal lengths, but inequal strings";
+ }
+
+ /* Test UsefulOutBuf_Swap() */
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 4, 8);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("efghabcd"))) {
+ return "swap fail 1";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 1, 2);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("bacdefgh"))) {
+ return "swap fail 2";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 1, 8);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("bcdefgha"))) {
+ return "swap fail 3";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 3, 4);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("dabcefgh"))) {
+ return "swap fail 4";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 9, 10, 11);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 5";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 4, 11);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 6";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 9, 0, 0);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 7";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 0, 0);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 8";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 8, 4, 0);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 9";
+ }
+
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abcdefgh");
+ UsefulOutBuf_Swap(&UOB, 0, 8, 4);
+ Out = UsefulOutBuf_OutUBuf(&UOB);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcdefgh"))) {
+ return "swap fail 10";
+ }
+
+
+ /* Test for UsefulOutBuf_GetOutput() */
+ UsefulOutBuf_Reset(&UOB);
+ UsefulOutBuf_AppendString(&UOB, "abc");
+ UsefulOutBuf_AppendString(&UOB, "xyz");
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 0);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("abcxyz"))) {
+ return "GetOutput fail 1";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 5);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("z"))) {
+ return "GetOutput fail 2";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 1);
+ if(UsefulBuf_Compare(Out, UsefulBuf_FROM_SZ_LITERAL("bcxyz"))) {
+ return "GetOutput fail 3";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 6);
+ if(!UsefulBuf_IsNULLC(Out)) {
+ return "GetOutput fail 4";
+ }
+
+ Out = UsefulOutBuf_OutUBufOffset(&UOB, 7);
+ if(!UsefulBuf_IsNULLC(Out)) {
+ return "GetOutput fail 5";
+ }
+
+ return NULL;
+}
diff --git a/test/UsefulBuf_Tests.h b/test/UsefulBuf_Tests.h
index 828f3f4..c0317e8 100644
--- a/test/UsefulBuf_Tests.h
+++ b/test/UsefulBuf_Tests.h
@@ -52,4 +52,6 @@
const char * UBAdvanceTest(void);
+const char * UOBExtraTests(void);
+
#endif
diff --git a/test/float_tests.c b/test/float_tests.c
index 97f92bf..ce078d4 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -41,16 +41,16 @@
#include "half_to_double_from_rfc7049.h"
-struct DoubleTestCase {
+struct FloatTestCase {
double dNumber;
- double fNumber;
+ float fNumber;
UsefulBufC Preferred;
UsefulBufC NotPreferred;
UsefulBufC CDE;
UsefulBufC DCBOR;
};
-/* Boundaries for all destination conversions to test at.
+/* Boundaries for destination conversions:
*
* smallest subnormal single 1.401298464324817e-45 2^^-149
* largest subnormal single 1.1754942106924411e-38 2^^-126
@@ -62,29 +62,34 @@
* smallest normal half 6.103515625E-5
* largest half 65504.0
*
- * Boundaries for origin conversions
+ * Boundaries for origin conversions:
* smallest subnormal double 5.0e-324 2^^-1074
* largest subnormal double
* smallest normal double 2.2250738585072014e-308 2^^-1022
* largest normal double 1.7976931348623157e308 2^^-1023
+ *
+ * Boundaries for double conversion to 64-bit integer:
+ * exponent 51, 52 significand bits set 4503599627370495
+ * exponent 52, 52 significand bits set 9007199254740991
+ * exponent 53, 52 bits set in significand 18014398509481982
*/
/* Always four lines per test case so shell scripts can process into
- * other formats. CDE and DCBOR standards are not complete yet,
- * encodings are a guess. C string literals are used because they
+ * other formats. CDE and DCBOR standards are not complete yet,
+ * encodings are what is expected. C string literals are used because they
* are the shortest notation. They are used __with a length__ . Null
* termination doesn't work because there are zero bytes.
*/
-static const struct DoubleTestCase DoubleTestCases[] = {
+static const struct FloatTestCase FloatTestCases[] = {
/* Zero */
{0.0, 0.0f,
{"\xF9\x00\x00", 3}, {"\xFB\x00\x00\x00\x00\x00\x00\x00\x00", 9},
- {"\xF9\x00\x00", 3}, {"\xF9\x00\x00", 3}},
+ {"\xF9\x00\x00", 3}, {"\x00", 1}},
/* Negative Zero */
{-0.0, -0.0f,
{"\xF9\x80\x00", 3}, {"\xFB\x80\x00\x00\x00\x00\x00\x00\x00", 9},
- {"\xF9\x80\x00", 3}, {"\xF9\x80\x00", 3}},
+ {"\xF9\x80\x00", 3}, {"\x00", 1}},
/* NaN */
{NAN, NAN,
@@ -104,12 +109,12 @@
/* 1.0 */
{1.0, 1.0f,
{"\xF9\x3C\x00", 3}, {"\xFB\x3F\xF0\x00\x00\x00\x00\x00\x00", 9},
- {"\xF9\x3C\x00", 3}, {"\xF9\x3C\x00", 3}},
+ {"\xF9\x3C\x00", 3}, {"\x01", 1}},
- /* -2.0 -- a negative number that is not zero */
+ /* -2.0 -- a negative */
{-2.0, -2.0f,
{"\xF9\xC0\x00", 3}, {"\xFB\xC0\x00\x00\x00\x00\x00\x00\x00", 9},
- {"\xF9\xC0\x00", 3}, {"\xF9\x3C\x00", 3}},
+ {"\xF9\xC0\x00", 3}, {"\x21", 1}},
/* 1/3 */
{0.333251953125, 0.333251953125f,
@@ -129,45 +134,44 @@
/* 6.097555160522461E-5 -- largest half-precision subnormal */
{6.097555160522461E-5, 0.0f,
{"\xF9\x03\xFF", 3}, {"\xFB\x3F\x0F\xF8\x00\x00\x00\x00\x00", 9},
- {"\xF9\x03\xFF", 3}, {"\xF9\04\00", 3}},
-
- /* 6.103515625E-5 -- smallest possible half-precision normal */
- {6.103515625E-5, 0.0f,
- {"\xF9\04\00", 3}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x00", 9},
- {"\xF9\04\00", 3}, {"\xF9\04\00", 3}},
-
- /* 6.1035156250000014E-5 -- slightly larger than smallest half-precision normal */
- {6.1035156250000014E-5, 6.1035156250000014E-5f,
- {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9},
- {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}},
+ {"\xF9\x03\xFF", 3}, {"\xF9\x03\xFF", 3}},
/* 6.1035156249999993E-5 -- slightly smaller than smallest half-precision normal */
{6.1035156249999993E-5, 0.0f,
{"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
{"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
- /* 65504.0 -- largest possible half-precision */
+ /* 6.103515625E-5 -- smallest half-precision normal */
+ {6.103515625E-5, 0.0f,
+ {"\xF9\04\00", 3}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x00", 9},
+ {"\xF9\04\00", 3}, {"\xF9\04\00", 3}},
+
+ /* 6.1035156250000014E-5 -- slightly larger than smallest half-precision normal */
+ {6.1035156250000014E-5, 0.0f,
+ {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9},
+ {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}},
+
+ /* 65504.0 -- largest half-precision */
{65504.0, 0.0f,
{"\xF9\x7B\xFF", 3}, {"\xFB\x40\xEF\xFC\x00\x00\x00\x00\x00", 9},
- {"\xF9\x7B\xFF", 3}, {"\xF9\x7B\xFF", 3}},
+ {"\xF9\x7B\xFF", 3}, {"\x19\xFF\xE0", 3}},
- /* 65504.1 -- exponent too large and too much precision to convert */
+ /* 65504.1 -- exponent too large and too much precision to convert to half */
{65504.1, 0.0f,
{"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9}, {"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9},
{"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9}, {"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9}},
- /* 65536.0 -- exponent too large but not too much precision for single */
+ /* 65536.0 -- exponent too large for half but not too much precision for single */
{65536.0, 65536.0f,
{"\xFA\x47\x80\x00\x00", 5}, {"\xFB\x40\xF0\x00\x00\x00\x00\x00\x00", 9},
- {"\xFA\x47\x80\x00\x00", 5}, {"\xFA\x47\x80\x00\x00", 5}},
+ {"\xFA\x47\x80\x00\x00", 5}, {"\x1A\x00\x01\x00\x00", 5}},
/* 1.401298464324817e-45 -- smallest single subnormal */
{1.401298464324817e-45, 1.40129846E-45f,
{"\xFA\x00\x00\x00\x01", 5}, {"\xFB\x36\xA0\x00\x00\x00\x00\x00\x00", 9},
{"\xFA\x00\x00\x00\x01", 5}, {"\xFA\x00\x00\x00\x01", 5}},
- /* 5.8774717541114375E-39 -- slightly smaller than the smallest
- // single normal */
+ /* 5.8774717541114375E-39 -- slightly smaller than the smallest single normal */
{5.8774717541114375E-39, 5.87747175E-39f,
{"\xFA\x00\x40\x00\x00", 5}, {"\xFB\x38\x00\x00\x00\x00\x00\x00\x00", 9},
{"\xFA\x00\x40\x00\x00", 5}, {"\xFA\x00\x40\x00\x00", 5}},
@@ -192,20 +196,100 @@
{"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9},
{"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9}},
+ /* 8388607 -- exponent 22 to test single exponent boundary */
+ {8388607, 8388607.0f,
+ {"\xFA\x4A\xFF\xFF\xFE", 5}, {"\xFB\x41\x5F\xFF\xFF\xC0\x00\x00\x00", 9},
+ {"\xFA\x4A\xFF\xFF\xFE", 5}, {"\x1A\x00\x7F\xFF\xFF", 5}},
+
+ /* 16777215 -- exponent 23 to test single exponent boundary */
+ {16777215, 16777215.0f,
+ {"\xFA\x4B\x7F\xFF\xFF", 5}, {"\xFB\x41\x6F\xFF\xFF\xE0\x00\x00\x00", 9},
+ {"\xFA\x4B\x7F\xFF\xFF", 5}, {"\x1A\x00\xFF\xFF\xFF", 5}},
+
/* 16777216 -- converts to single without loss */
- {16777216, 16777216,
+ {16777216, 16777216.0f,
{"\xFA\x4B\x80\x00\x00", 5}, {"\xFB\x41\x70\x00\x00\x00\x00\x00\x00", 9},
- {"\xFA\x4B\x80\x00\x00", 5}, {"\xFA\x4B\x80\x00\x00", 5}},
+ {"\xFA\x4B\x80\x00\x00", 5}, {"\x1A\x01\x00\x00\x00", 5}},
- /* 16777217 -- one more than above and fails conversion to single */
- {16777217, 16777216,
+ /* 16777217 -- one more than above and fails conversion to single because of precision */
+ {16777217, 0.0f,
{"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}, {"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9},
- {"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}, {"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}},
+ {"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}, {"\x1A\x01\x00\x00\x01", 5}},
+
+ /* 33554430 -- exponent 24 to test single exponent boundary */
+ {33554430, 33554430.0f,
+ {"\xFA\x4B\xFF\xFF\xFF", 5}, {"\xFB\x41\x7F\xFF\xFF\xE0\x00\x00\x00", 9},
+ {"\xFA\x4B\xFF\xFF\xFF", 5}, {"\x1A\x01\xFF\xFF\xFE", 5}},
- /* 3.4028234663852886E+38 -- largest possible single normal */
+ /* 4294967295 -- 2^^32 - 1 UINT32_MAX */
+ {4294967295, 0,
+ {"\xFB\x41\xEF\xFF\xFF\xFF\xE0\x00\x00", 9}, {"\xFB\x41\xEF\xFF\xFF\xFF\xE0\x00\x00", 9},
+ {"\xFB\x41\xEF\xFF\xFF\xFF\xE0\x00\x00", 9}, {"\x1A\xFF\xFF\xFF\xFF", 5}},
+
+ /* 4294967296 -- 2^^32, UINT32_MAX + 1 */
+ {4294967296, 4294967296.0f,
+ {"\xFA\x4F\x80\x00\x00", 5}, {"\xFB\x41\xF0\x00\x00\x00\x00\x00\x00", 9},
+ {"\xFA\x4F\x80\x00\x00", 5}, {"\x1B\x00\x00\x00\x01\x00\x00\x00\x00", 9}},
+
+ /* 2251799813685248 -- exponent 51, 0 significand bits set, to test double exponent boundary */
+ {2251799813685248, 2251799813685248.0f,
+ {"\xFA\x59\x00\x00\x00", 5}, {"\xFB\x43\x20\x00\x00\x00\x00\x00\x00", 9},
+ {"\xFA\x59\x00\x00\x00", 5}, {"\x1B\x00\x08\x00\x00\x00\x00\x00\x00", 9}},
+
+ /* 4503599627370495 -- exponent 51, 52 significand bits set to test double exponent boundary*/
+ {4503599627370495, 0,
+ {"\xFB\x43\x2F\xFF\xFF\xFF\xFF\xFF\xFE", 9}, {"\xFB\x43\x2F\xFF\xFF\xFF\xFF\xFF\xFE", 9},
+ {"\xFB\x43\x2F\xFF\xFF\xFF\xFF\xFF\xFE", 9}, {"\x1B\x00\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+
+ /* 9007199254740991 -- exponent 52, 52 significand bits set to test double exponent boundary */
+ {9007199254740991, 0,
+ {"\xFB\x43\x3F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x43\x3F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+ {"\xFB\x43\x3F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x1B\x00\x1F\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+
+ /* 18014398509481982 -- exponent 53, 52 bits set in significand (double lacks precision to represent 18014398509481983) */
+ {18014398509481982, 0,
+ {"\xFB\x43\x4F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x43\x4F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+ {"\xFB\x43\x4F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x1B\x00\x3F\xFF\xFF\xFF\xFF\xFF\xFE", 9}},
+
+ /* 18014398509481984 -- next largest possible double above 18014398509481982 */
+ {18014398509481984, 18014398509481984.0f,
+ {"\xFA\x5A\x80\x00\x00", 5}, {"\xFB\x43\x50\x00\x00\x00\x00\x00\x00", 9},
+ {"\xFA\x5A\x80\x00\x00", 5}, {"\x1B\x00\x40\x00\x00\x00\x00\x00\x00", 9}},
+
+ /* 18446742974197924000.0.0 -- largest single that can convert to uint64 */
+ {18446742974197924000.0, 18446742974197924000.0f,
+ {"\xFA\x5F\x7F\xFF\xFF", 5}, {"\xFB\x43\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
+ {"\xFA\x5F\x7F\xFF\xFF", 5}, {"\x1B\xFF\xFF\xFF\x00\x00\x00\x00\x00", 9}},
+
+ /* 18446744073709550000.0 -- largest double that can convert to uint64, almost UINT64_MAX (18446744073709551615) */
+ {18446744073709550000.0, 0,
+ {"\xFB\x43\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x43\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+ {"\xFB\x43\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x1B\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00", 9}},
+
+ /* 18446744073709552000.0 -- just too large to convert to uint64, but converts to a single, just over UINT64_MAX */
+ {18446744073709552000.0, 18446744073709552000.0f,
+ {"\xFA\x5F\x80\x00\x00", 5}, {"\xFB\x43\xF0\x00\x00\x00\x00\x00\x00", 9},
+ {"\xFA\x5F\x80\x00\x00", 5}, {"\xFA\x5F\x80\x00\x00", 5}},
+
+ /* -4294967295 -- negative UINT32_MAX */
+ {-4294967295.0, 0,
+ {"\xFB\xC1\xEF\xFF\xFF\xFF\xE0\x00\x00", 9}, {"\xFB\xC1\xEF\xFF\xFF\xFF\xE0\x00\x00", 9},
+ {"\xFB\xC1\xEF\xFF\xFF\xFF\xE0\x00\x00", 9}, {"\x3A\xFF\xFF\xFF\xFE", 5}},
+
+ /* -9223372036854774784.0 -- most negative double that converts to int64 */
+ {-9223372036854774784.0, 0,
+ {"\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,
+ {"\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}},
+
+ /* 3.4028234663852886E+38 -- largest possible single */
{3.4028234663852886E+38, 3.40282347E+38f,
- {"\xFA\x7F\x7F\xFF\xFF", 5}, {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
- {"\xFA\x7F\x7F\xFF\xFF", 5}, {"\xFA\x7F\x7F\xFF\xFF", 5}},
+ {"\xFA\x7F\x7F\xFF\xFF", 5}, {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
+ {"\xFA\x7F\x7F\xFF\xFF", 5}, {"\xFA\x7F\x7F\xFF\xFF", 5}},
/* 3.402823466385289E+38 -- slightly larger than largest possible single */
{3.402823466385289E+38, 0.0f,
@@ -242,9 +326,12 @@
};
+/* Can't use types double and float here because there's no way in C to
+ * construct arbitrary payloads for those types.
+ */
struct NaNTestCase {
- uint64_t uDouble;
- uint32_t uSingle;
+ uint64_t uDouble; /* Converted to double in test */
+ uint32_t uSingle; /* Converted to single in test */
UsefulBufC Preferred;
UsefulBufC NotPreferred;
UsefulBufC CDE;
@@ -292,14 +379,13 @@
/* Payload with all bits set */
{0x7fffffffffffffff, 0x00000000,
{"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
- {"\xF9\x7E\x00", 3}, {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+ {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
/* List terminator */
{0, 0, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} }
};
-
/* Public function. See float_tests.h
*
* This is the main test of floating-point encoding / decoding. It is
@@ -311,7 +397,7 @@
FloatValuesTests(void)
{
unsigned int uTestIndex;
- const struct DoubleTestCase *pTestCase;
+ const struct FloatTestCase *pTestCase;
const struct NaNTestCase *pNaNTestCase;
MakeUsefulBufOnStack( TestOutBuffer, 20);
UsefulBufC TestOutput;
@@ -325,12 +411,11 @@
#endif
/* Test a variety of doubles */
- for(uTestIndex = 0; DoubleTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
- pTestCase = &DoubleTestCases[uTestIndex];
+ for(uTestIndex = 0; FloatTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
+ pTestCase = &FloatTestCases[uTestIndex];
- // if(pTestCase->dNumber == 1.1754943508222874E-38) {
- if(uTestIndex == 19) {
- uErr = 99; /* For setting break points for particular tests */
+ if(uTestIndex == 34) {
+ uDecoded = 1;
}
/* Number Encode of Preferred */
@@ -357,6 +442,47 @@
return MakeTestResultCode(uTestIndex, 2, 200);
}
+ /* Number Encode of CDE */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_SerializationCDE(&EnCtx);
+ QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 20, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pTestCase->CDE)) {
+ return MakeTestResultCode(uTestIndex, 21, 200);
+ }
+
+ /* Number Encode of dCBOR */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_SerializationdCBOR(&EnCtx);
+ QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 22, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pTestCase->DCBOR)) {
+ return MakeTestResultCode(uTestIndex, 23, 200);
+ }
+
+ if(pTestCase->fNumber != 0) {
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_SerializationdCBOR(&EnCtx);
+ QCBOREncode_AddFloat(&EnCtx, pTestCase->fNumber);
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 24, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pTestCase->DCBOR)) {
+ return MakeTestResultCode(uTestIndex, 25, 200);
+ }
+ }
+
+
/* Number Decode of Preferred */
QCBORDecode_Init(&DCtx, pTestCase->Preferred, 0);
uErr = QCBORDecode_GetNext(&DCtx, &Item);
@@ -382,32 +508,32 @@
* indicates single-precision in the encoded CBOR. */
if(pTestCase->Preferred.len == 5) {
if(Item.uDataType != QCBOR_TYPE_FLOAT) {
- return MakeTestResultCode(uTestIndex, 4, 0);
+ return MakeTestResultCode(uTestIndex, 41, 0);
}
if(isnan(pTestCase->dNumber)) {
if(!isnan(Item.val.fnum)) {
- return MakeTestResultCode(uTestIndex, 5, 0);
+ return MakeTestResultCode(uTestIndex, 51, 0);
}
} else {
if(Item.val.fnum != pTestCase->fNumber) {
- return MakeTestResultCode(uTestIndex, 6, 0);
+ return MakeTestResultCode(uTestIndex, 61, 0);
}
}
} else {
if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
- return MakeTestResultCode(uTestIndex, 4, 0);
+ return MakeTestResultCode(uTestIndex, 42, 0);
}
if(isnan(pTestCase->dNumber)) {
if(!isnan(Item.val.dfnum)) {
- return MakeTestResultCode(uTestIndex, 5, 0);
+ return MakeTestResultCode(uTestIndex, 52, 0);
}
} else {
if(Item.val.dfnum != pTestCase->dNumber) {
- return MakeTestResultCode(uTestIndex, 6, 0);
+ return MakeTestResultCode(uTestIndex, 62, 0);
}
}
}
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
/* Number Decode of Not Preferred */
QCBORDecode_Init(&DCtx, pTestCase->NotPreferred, 0);
@@ -441,6 +567,7 @@
/* NaN Encode of Preferred */
QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
if(uErr != QCBOR_SUCCESS) {
@@ -489,6 +616,7 @@
/* NaN Encode of Not Preferred */
QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
QCBOREncode_AddDoubleNoPreferred(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
if(uErr != QCBOR_SUCCESS) {
@@ -498,8 +626,9 @@
return MakeTestResultCode(uTestIndex+100, 11, 200);
}
- /* NaN Decode of Preferred */
+ /* NaN Decode of Not Preferred */
QCBORDecode_Init(&DCtx, pNaNTestCase->Preferred, 0);
+ QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
uErr = QCBORDecode_GetNext(&DCtx, &Item);
if(uErr != QCBOR_SUCCESS) {
return MakeTestResultCode(uTestIndex+100, 12, uErr);
@@ -535,6 +664,7 @@
/* NaN Decode of Not Preferred */
QCBORDecode_Init(&DCtx, pNaNTestCase->NotPreferred, 0);
+ QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
uErr = QCBORDecode_GetNext(&DCtx, &Item);
if(uErr != QCBOR_SUCCESS) {
return MakeTestResultCode(uTestIndex+100, 13, uErr);
@@ -543,6 +673,21 @@
if(uDecoded != pNaNTestCase->uDouble) {
return MakeTestResultCode(uTestIndex+100, 13, 200);
}
+
+
+ /* NaN Encode of DCBOR */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Allow(&EnCtx, QCBOR_ENCODE_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_SerializationdCBOR(&EnCtx);
+ QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 14, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->DCBOR)) {
+ return MakeTestResultCode(uTestIndex+100, 14, 200);
+ }
+
}
return 0;
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 0f48613..6b97768 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -132,7 +132,9 @@
*/
static const uint8_t spExpectedEncodedInts[] = {
- 0x98, 0x2f, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff,
+ 0x98, 0x31, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3b, 0xFf, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xfe, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x3b, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff,
0xff, 0x3a, 0xff, 0xff, 0xff, 0xfe, 0x3a, 0xff,
@@ -172,6 +174,18 @@
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
return (int32_t)nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
+ Item.val.uint64 != 18446744073709551615ULL)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return (int32_t)nCBORError;
+ if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
+ Item.val.uint64 != 18446744073709551614ULL)
+ return -1;
+
+ if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ return (int32_t)nCBORError;
if(Item.uDataType != QCBOR_TYPE_INT64 ||
Item.val.int64 != -9223372036854775807LL - 1)
return -1;
@@ -487,14 +501,6 @@
}
-/* One less than the smallest negative integer allowed in C. Decoding
- this should fail.
- -9223372036854775809
- */
-static const uint8_t spTooSmallNegative[] = {
- 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
/*
Tests the decoding of lots of different integers sizes
@@ -515,16 +521,6 @@
return nReturn;
}
- // The one large negative integer that can be parsed
- QCBORDecode_Init(&DCtx,
- UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spTooSmallNegative),
- QCBOR_DECODE_MODE_NORMAL);
-
- QCBORItem item;
- if(QCBORDecode_GetNext(&DCtx, &item) != QCBOR_ERR_INT_OVERFLOW) {
- nReturn = -4000;
- }
-
return(nReturn);
}
@@ -6637,13 +6633,12 @@
*/
static const uint8_t spRecoverableMapErrors[] = {
#ifndef QCBOR_DISABLE_TAGS
- 0xa6,
+ 0xa5,
0x04, 0xc1, 0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c,
0x01, 0xd8, 0xe0, 0xd8, 0xe1, 0xd8, 0xe2, 0xd8, 0xe3, 0xd8, 0x04, 0x00,
#else
0xa4,
#endif
- 0x03, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x05, 0x00,
0x05, 0x00,
0x08, 0x08,
@@ -7051,12 +7046,6 @@
#endif
- QCBORDecode_GetInt64InMapN(&DCtx, 0x03, &nInt);
- uErr = QCBORDecode_GetAndResetError(&DCtx);
- if(uErr != QCBOR_ERR_INT_OVERFLOW) {
- return 2023;
- }
-
#ifndef QCBOR_DISABLE_TAGS
QCBORDecode_GetEpochDateInMapN(&DCtx, 0x04, QCBOR_TAG_REQUIREMENT_TAG, &nInt);
uErr = QCBORDecode_GetAndResetError(&DCtx);
@@ -7558,8 +7547,8 @@
FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
},
{
- "Negative integer -18446744073709551616",
- {(uint8_t[]){0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 9},
+ "Negative integer -9223372036854775808",
+ {(uint8_t[]){0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 9},
-9223372036854775807-1, // INT64_MIN
QCBOR_SUCCESS,
0ULL,
@@ -7568,6 +7557,16 @@
FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
},
{
+ "Negative integer -18446744073709551616",
+ {(uint8_t[]){0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 9},
+ 0ULL,
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,
+ 0ULL,
+ QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+ -18446744073709551616.0,
+ FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
+ },
+ {
"Double Floating point value 100.3",
{(uint8_t[]){0xfb, 0x40, 0x59, 0x13, 0x33, 0x33, 0x33, 0x33, 0x33}, 9},
100L,
@@ -7630,6 +7629,9 @@
int32_t IntegerConvertTest(void)
{
+ uint64_t uInt;
+
+
const int nNumTests = C_ARRAY_COUNT(NumberConversions,
struct NumberConversion);
@@ -7660,7 +7662,6 @@
return (int32_t)(3333+nIndex);
}
- uint64_t uInt;
QCBORDecode_GetUInt64ConvertAll(&DCtx, 0xffff, &uInt);
if(QCBORDecode_GetError(&DCtx) != pF->uErrorUint64) {
return (int32_t)(4000+nIndex);
@@ -9602,6 +9603,214 @@
}
+#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+
+struct PreciseNumberConversion {
+ char *szDescription;
+ UsefulBufC CBOR;
+ QCBORError uError;
+ uint8_t qcborType;
+ struct {
+ int64_t int64;
+ uint64_t uint64;
+ double d;
+ } number;
+};
+
+
+static const struct PreciseNumberConversion PreciseNumberConversions[] = {
+ {
+ "-0.00",
+ {"\xf9\x80\x00", 3},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {0, 0, 0}
+ },
+ {
+ "NaN",
+ {"\xf9\x7e\x00", 3},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, NAN}
+ },
+ {
+ "NaN payload",
+ {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, NAN}
+ },
+ {
+ "65536.0 single",
+ {"\xFA\x47\x80\x00\x00", 5},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {65536, 0, 0}
+ },
+ {
+ "Infinity",
+ {"\xf9\x7c\x00", 3},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, INFINITY}
+ },
+ {
+ "1.0",
+ {"\xf9\x3c\x00", 3},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {1, 0, 0}
+ },
+ {
+ "UINT64_MAX",
+ {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_UINT64,
+ {0, UINT64_MAX, 0}
+ },
+ {
+ "INT64_MIN",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {INT64_MIN, 0, 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},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709547521.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}
+ },
+ {
+ "65-bit neg too precise",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xfc\x00", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 18446744073709550592ULL, 0.0}
+ },
+ {
+ "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}
+ },
+ {
+ "Zero",
+ {"\x00", 1},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {0, 0, 0}
+ },
+ {
+ "Pi",
+ {"\xFB\x40\x09\x2A\xDB\x40\x2D\x16\xB9", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, 3.145926}
+ },
+ {
+ "String",
+ {"\x60", 1},
+ QCBOR_ERR_UNEXPECTED_TYPE,
+ QCBOR_TYPE_NONE,
+ {0, 0, 0}
+ }
+};
+
+
+int32_t
+PreciseNumbersTest(void)
+{
+ int i;
+ QCBORError uErr;
+ QCBORItem Item;
+ QCBORDecodeContext DCtx;
+ const struct PreciseNumberConversion *pTest;
+
+ const int count = (int)C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+
+ for(i = 0; i < count; i++) {
+ pTest = &PreciseNumberConversions[i];
+
+ if(i == 11) {
+ uErr = 99; // For break point only
+ }
+
+ QCBORDecode_Init(&DCtx, pTest->CBOR, 0);
+
+ QCBORDecode_GetNumberConvertPrecisely(&DCtx, &Item);
+
+ uErr = QCBORDecode_GetError(&DCtx);
+
+ if(uErr != pTest->uError) {
+ return i * 1000 + (int)uErr;
+ }
+
+ if(pTest->qcborType != Item.uDataType) {
+ return i * 1000 + 200;
+ }
+
+ if(pTest->qcborType == QCBOR_TYPE_NONE) {
+ continue;
+ }
+
+ switch(pTest->qcborType) {
+ case QCBOR_TYPE_INT64:
+ if(Item.val.int64 != pTest->number.int64) {
+ return i * 1000 + 300;
+ }
+ break;
+
+ case QCBOR_TYPE_UINT64:
+ case QCBOR_TYPE_65BIT_NEG_INT:
+ if(Item.val.uint64 != pTest->number.uint64) {
+ return i * 1000 + 400;
+ }
+ break;
+
+ case QCBOR_TYPE_DOUBLE:
+ if(isnan(pTest->number.d)) {
+ if(!isnan(Item.val.dfnum)) {
+ return i * 1000 + 600;
+ }
+ } else {
+ if(Item.val.dfnum != pTest->number.d) {
+ return i * 1000 + 500;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
static const uint8_t spExpectedArray2s[] = {
0x82, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index a08a3af..4c4c0a6 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -319,6 +319,7 @@
int32_t CBORTestIssue134(void);
+int32_t PreciseNumbersTest(void);
int32_t ErrorHandlingTests(void);
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index be5548c..1c0902b 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -48,7 +48,7 @@
*/
-//#define PRINT_FUNCTIONS_FOR_DEBUGGING
+#define PRINT_FUNCTIONS_FOR_DEBUGGING
#ifdef PRINT_FUNCTIONS_FOR_DEBUGGING
#include <stdio.h>
@@ -96,13 +96,9 @@
#endif
-#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/*
Returns 0 if UsefulBufs are equal
Returns 1000000 + offeset if they are not equal.
-
-
-
*/
struct UBCompareDiagnostic {
uint8_t uActual;
@@ -130,7 +126,6 @@
return 0;
}
-#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
// One big buffer that is used by all the tests to encode into
@@ -281,10 +276,10 @@
static const uint8_t spExpectedEncodedAll[] = {
- 0x98, 0x23, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
+ 0x98, 0x24, 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, 0xa4, 0x63, 0x4c, 0x42,
+ 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0x20, 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,
@@ -526,8 +521,9 @@
QCBOREncode_AddSZString(pECtx, "INT64");
QCBOREncode_AddTag(pECtx, 76);
QCBOREncode_AddInt64(pECtx, 77689989909);
- QCBOREncode_AddUInt64(pECtx,0);
+ QCBOREncode_AddUInt64(pECtx, 0);
QCBOREncode_AddInt64(pECtx, -44);
+ QCBOREncode_AddNegativeUInt64(pECtx, 0);
/* ints that go in maps */
QCBOREncode_OpenMap(pECtx);
@@ -696,13 +692,18 @@
/* Improvement: this test should be broken down into several so it is more
* managable. Tags and labels could be more sensible */
QCBOREncodeContext ECtx;
- int nReturn = 0;
+ UsefulBufC Enc;
+ size_t size;
+ int nReturn;
+ QCBORError uExpectedErr;
+
+ nReturn = 0;
QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_Allow(&ECtx, QCBOR_ENCODE_ALLOW_ALL);
- AddAll (&ECtx);
+ AddAll(&ECtx);
- UsefulBufC Enc;
if(QCBOREncode_Finish(&ECtx, &Enc)) {
nReturn = -1;
goto Done;
@@ -710,15 +711,16 @@
if(CheckResults(Enc, spExpectedEncodedAll)) {
nReturn = -2;
+ goto Done;
}
/* Also test size calculation */
QCBOREncode_Init(&ECtx, SizeCalculateUsefulBuf);
+ QCBOREncode_Allow(&ECtx, QCBOR_ENCODE_ALLOW_ALL);
- AddAll (&ECtx);
+ AddAll(&ECtx);
- size_t size;
if(QCBOREncode_FinishGetSize(&ECtx, &size)) {
nReturn = -10;
goto Done;
@@ -726,8 +728,55 @@
if(size != sizeof(spExpectedEncodedAll)) {
nReturn = -11;
+ goto Done;
}
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ uExpectedErr = QCBOR_ERR_NOT_ALLOWED;
+#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;
+ }
+
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+ QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ /* 0x7ff8000000000001ULL is a NaN with a payload. */
+ QCBOREncode_AddDouble(&ECtx, UsefulBufUtil_CopyUint64ToDouble(0x7ff8000000000001ULL));
+ if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
+ nReturn = -22;
+ goto Done;
+ }
+
+
+ /* 0x7ffc000000000000ULL is a NaN with a payload. */
+ QCBOREncode_AddDouble(&ECtx, UsefulBufUtil_CopyUint64ToDouble(0x7ff8000000000001ULL));
+ if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
+ nReturn = -23;
+ goto Done;
+ }
+
+ /* 0x7ff80001UL is a NaN with a payload. */
+ QCBOREncode_AddFloat(&ECtx, UsefulBufUtil_CopyUint32ToFloat(0x7ff80001UL));
+ if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
+ nReturn = -24;
+ goto Done;
+ }
+
+ /* 0x7ffc0000UL is a NaN with a payload. */
+ QCBOREncode_AddFloat(&ECtx, UsefulBufUtil_CopyUint32ToFloat(0x7ffc0000UL));
+ if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
+ nReturn = -25;
+ goto Done;
+ }
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+
Done:
return nReturn;
}
@@ -1768,12 +1817,6 @@
*/
static const uint8_t spExpectedBstrWrap[] = {0x82, 0x19, 0x01, 0xC3, 0x43, 0x19, 0x01, 0xD2};
-/*
- 81 #array(1)
- 0x58 0x25 # string of length 37 (length of "This is longer than twenty four bytes")
- */
-static const uint8_t spExpectedTypeAndLen[] = {0x81, 0x58, 0x25};
-
static const uint8_t spExpectedForBstrWrapCancel[] = {0x82, 0x19, 0x01, 0xC3, 0x18, 0x2A};
/*
@@ -1823,19 +1866,6 @@
return -5;
}
- // Third, test QCBOREncode_AddBytesLenOnly() here as it is part of the
- // bstr wrapping use cases.
- UsefulBuf_MAKE_STACK_UB(StuffBuf, 50);
- QCBOREncode_Init(&EC, StuffBuf);
- QCBOREncode_OpenArray(&EC);
- QCBOREncode_AddBytesLenOnly(&EC, UsefulBuf_FROM_SZ_LITERAL("This is longer than twenty four bytes"));
- QCBOREncode_CloseArray(&EC);
- if(QCBOREncode_Finish(&EC, &Encoded)) {
- return -6;
- }
- if(CheckResults(Encoded, spExpectedTypeAndLen)) {
- return -7;
- }
// Fourth test, cancelling a byte string
QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
@@ -3109,3 +3139,573 @@
return 0;
}
+
+
+
+int32_t
+SortMapTest(void)
+{
+ UsefulBuf_MAKE_STACK_UB( TestBuf, 200);
+ QCBOREncodeContext EC;
+ UsefulBufC EncodedAndSorted;
+ QCBORError uErr;
+ struct UBCompareDiagnostic CompareDiagnostics;
+
+
+ /* --- Basic sort test case --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 3, 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 4, 4);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 11;
+ }
+
+ static const uint8_t spBasic[] = {
+ 0xA4, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04};
+
+ if(UsefulBuf_Compare(EncodedAndSorted, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBasic))) {
+ return 12;
+ }
+
+ /* --- Empty map sort test case --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 21;
+ }
+
+ static const uint8_t spEmpty[] = {0xA0};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spEmpty),
+ &CompareDiagnostics)) {
+ return 22;
+ }
+
+ /* --- Several levels of nested sorted maps --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "three", 3);
+ QCBOREncode_OpenMapInMapN(&EC, 428);
+ QCBOREncode_AddNULLToMap(&EC, "null");
+ QCBOREncode_OpenArrayInMap(&EC, "array");
+ QCBOREncode_AddSZString(&EC, "hi");
+ QCBOREncode_AddSZString(&EC, "there");
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty2");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty1");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddDateEpochToMapN(&EC, 88, 888888);
+ QCBOREncode_AddBoolToMap(&EC, "boo", true);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 31;
+ }
+
+ /* Correctly sorted.
+ * {
+ * 88: 1(888888),
+ * 428: {
+ * "null": null,
+ * "array": [
+ * "hi",
+ * "there"
+ * ],
+ * "empty1": {},
+ * "empty2": {}
+ * },
+ * "boo": true,
+ * "three": 3
+ * }
+ */
+ static const uint8_t spSorted[] = {
+ 0xA4, 0x18, 0x58, 0xC1, 0x1A, 0x00, 0x0D, 0x90,
+ 0x38, 0x19, 0x01, 0xAC, 0xA4, 0x64, 0x6E, 0x75,
+ 0x6C, 0x6C, 0xF6, 0x65, 0x61, 0x72, 0x72, 0x61,
+ 0x79, 0x82, 0x62, 0x68, 0x69, 0x65, 0x74, 0x68,
+ 0x65, 0x72, 0x65, 0x66, 0x65, 0x6D, 0x70, 0x74,
+ 0x79, 0x31, 0xA0, 0x66, 0x65, 0x6D, 0x70, 0x74,
+ 0x79, 0x32, 0xA0, 0x63, 0x62, 0x6F, 0x6F, 0xF5,
+ 0x65, 0x74, 0x68, 0x72, 0x65, 0x65, 0x03};
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSorted),
+ &CompareDiagnostics)) {
+ return 32;
+ }
+
+
+ /* Same data items, but added in a different order */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "three", 3);
+ QCBOREncode_OpenMapInMapN(&EC, 428);
+ QCBOREncode_OpenMapInMap(&EC, "empty1");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_OpenArrayInMap(&EC, "array");
+ QCBOREncode_AddSZString(&EC, "hi");
+ QCBOREncode_AddSZString(&EC, "there");
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty2");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddNULLToMap(&EC, "null");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddDateEpochToMapN(&EC, 88, 888888);
+ QCBOREncode_AddBoolToMap(&EC, "boo", true);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 31;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSorted),
+ &CompareDiagnostics)) {
+ return 32;
+ }
+
+ /* Same data items, but added in a different order */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddBoolToMap(&EC, "boo", true);
+ QCBOREncode_OpenMapInMapN(&EC, 428);
+ QCBOREncode_OpenMapInMap(&EC, "empty1");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_OpenArrayInMap(&EC, "array");
+ QCBOREncode_AddSZString(&EC, "hi");
+ QCBOREncode_AddSZString(&EC, "there");
+ QCBOREncode_CloseArray(&EC);
+ QCBOREncode_OpenMapInMap(&EC, "empty2");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddNULLToMap(&EC, "null");
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddDateEpochToMapN(&EC, 88, 888888);
+ QCBOREncode_AddInt64ToMap(&EC, "three", 3);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 31;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSorted),
+ &CompareDiagnostics)) {
+ return 32;
+ }
+
+
+
+ /* --- Degenerate case of everything in order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 3);
+ QCBOREncode_AddInt64ToMap(&EC, "b", 4);
+ QCBOREncode_AddInt64ToMap(&EC, "aa", 5);
+ QCBOREncode_AddInt64ToMap(&EC, "aaa", 6);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 41;
+ }
+
+ static const uint8_t sp6Items[] = {
+ 0xA7, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x61,
+ 0x61, 0x03, 0x61, 0x62, 0x04, 0x62, 0x61, 0x61,
+ 0x05, 0x63, 0x61, 0x61, 0x61, 0x06};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sp6Items),
+ &CompareDiagnostics)) {
+ return 42;
+ }
+
+ /* --- Degenerate case -- reverse order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "aaa", 6);
+ QCBOREncode_AddInt64ToMap(&EC, "aa", 5);
+ QCBOREncode_AddInt64ToMap(&EC, "b", 4);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 51;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sp6Items),
+ &CompareDiagnostics)) {
+ return 52;
+ }
+
+ /* --- Same items, randomly out of order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "aa", 5);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMap(&EC, "b", 4);
+ QCBOREncode_AddInt64ToMap(&EC, "aaa", 6);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 61;
+ }
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sp6Items),
+ &CompareDiagnostics)) {
+ return 62;
+ }
+
+ /* --- Stuff in front of and after array to sort --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddInt64(&EC, 111);
+ QCBOREncode_AddInt64(&EC, 222);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 0, 0);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ QCBOREncode_AddInt64(&EC, 888);
+ QCBOREncode_AddInt64(&EC, 999);
+ QCBOREncode_CloseArray(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 71;
+ }
+
+ static const uint8_t spPreItems[] = {
+ 0x85, 0x18, 0x6F, 0x18, 0xDE, 0xA3, 0x00, 0x00,
+ 0x01, 0x01, 0x02, 0x02, 0x19, 0x03, 0x78, 0x19,
+ 0x03, 0xE7};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spPreItems),
+ &CompareDiagnostics)) {
+ return 72;
+ }
+
+ /* --- map with labels of all CBOR major types and in reverse order --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+
+ /* Adding labels directly rather than AddToMap functions */
+
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+ QCBOREncode_AddDouble(&EC, 8.77);
+ QCBOREncode_AddInt64(&EC, 7);
+#endif /* QCBOR_DISABLE_ALL_FLOAT */
+
+ QCBOREncode_AddBool(&EC, true);
+ QCBOREncode_AddInt64(&EC, 6);
+
+ QCBOREncode_AddDateEpoch(&EC, 88);
+ QCBOREncode_AddInt64(&EC, 5);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\xa0"));
+ QCBOREncode_AddInt64(&EC, 4);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\x80"));
+ QCBOREncode_AddInt64(&EC, 7);
+
+ QCBOREncode_AddInt64ToMap(&EC, "text", 3);
+
+ QCBOREncode_AddBytes(&EC, UsefulBuf_FromSZ("xx"));
+ QCBOREncode_AddInt64(&EC, 2);
+
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1); /* Integer */
+ QCBOREncode_CloseAndSortMap(&EC);
+
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 81;
+ }
+
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+ static const uint8_t spLabelTypes[] = {
+ 0xA8, 0x01, 0x01, 0x42, 0x78, 0x78, 0x02, 0x64,
+ 0x74, 0x65, 0x78, 0x74, 0x03, 0x80, 0x07, 0xA0,
+ 0x04, 0xC1, 0x18, 0x58, 0x05, 0xF5, 0x06, 0xFB,
+ 0x40, 0x21, 0x8A, 0x3D, 0x70, 0xA3, 0xD7, 0x0A,
+ 0x07};
+#else
+ static const uint8_t spLabelTypes[] = {
+ 0xA7, 0x01, 0x01, 0x42, 0x78, 0x78, 0x02, 0x64,
+ 0x74, 0x65, 0x78, 0x74, 0x03, 0x80, 0x07, 0xA0,
+ 0x04, 0xC1, 0x18, 0x58, 0x05, 0xF5, 0x06};
+#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
+
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spLabelTypes),
+ &CompareDiagnostics)) {
+ return 82;
+ }
+
+ /* --- labels are indefinitely encoded --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+
+ QCBOREncode_AddInt64ToMap(&EC, "aaaa", 1);
+
+ QCBOREncode_AddInt64ToMap(&EC, "bb", 2);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\x7f\x61" "a" "\x61" "a" "\xff"));
+ QCBOREncode_AddInt64(&EC, 3);
+
+ QCBOREncode_AddEncoded(&EC, UsefulBuf_FromSZ("\x7f" "\x61" "c" "\xff"));
+ QCBOREncode_AddInt64(&EC, 4);
+
+ QCBOREncode_CloseAndSortMap(&EC);
+
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 91;
+ }
+
+ static const uint8_t spIndefItems[] = {
+ 0xA4, 0x62, 0x62, 0x62, 0x02, 0x64, 0x61, 0x61,
+ 0x61, 0x61, 0x01, 0x7F, 0x61, 0x61, 0x61, 0x61,
+ 0xFF, 0x03, 0x7F, 0x61, 0x63, 0xFF, 0x04};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndefItems),
+ &CompareDiagnostics)) {
+ return 92;
+ }
+
+ /* --- Indefinitely encoded maps --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMapIndefiniteLength(&EC);
+
+ QCBOREncode_OpenMapIndefiniteLengthInMap(&EC, "aa");
+ QCBOREncode_CloseMapIndefiniteLength(&EC);
+
+ QCBOREncode_OpenArrayIndefiniteLengthInMap(&EC, "ff");
+ QCBOREncode_CloseArrayIndefiniteLength(&EC);
+
+ QCBOREncode_OpenMapIndefiniteLengthInMap(&EC, "zz");
+ QCBOREncode_CloseMapIndefiniteLength(&EC);
+
+ QCBOREncode_OpenMapIndefiniteLengthInMap(&EC, "bb");
+ QCBOREncode_CloseMapIndefiniteLength(&EC);
+
+ QCBOREncode_CloseAndSortMapIndef(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr) {
+ return 101;
+ }
+
+ static const uint8_t spIndeMaps[] = {
+ 0xBF, 0x62, 0x61, 0x61, 0xBF, 0xFF, 0x62, 0x62,
+ 0x62, 0xBF, 0xFF, 0x62, 0x66, 0x66, 0x9F, 0xFF,
+ 0x62, 0x7A, 0x7A, 0xBF, 0xFF, 0xFF, 0x06, 0xFB};
+ if(UsefulBuf_CompareWithDiagnostic(EncodedAndSorted,
+ UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spIndeMaps),
+ &CompareDiagnostics)) {
+ return 102;
+ }
+
+
+ /* --- Duplicate label test --- */
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 3, 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 114;
+ }
+
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMapN(&EC, 3, 3);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 1);
+ QCBOREncode_AddInt64ToMapN(&EC, 1, 2);
+ QCBOREncode_AddInt64ToMapN(&EC, 2, 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 115;
+ }
+
+ QCBOREncode_Init(&EC, TestBuf);
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddInt64ToMap(&EC, "abc", 3);
+ QCBOREncode_AddInt64ToMap(&EC, "def", 1);
+ QCBOREncode_AddInt64ToMap(&EC, "def", 1);
+ QCBOREncode_AddInt64ToMap(&EC, "def", 2);
+ QCBOREncode_CloseAndSortMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &EncodedAndSorted);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 116;
+ }
+
+ return 0;
+}
+
+
+#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+
+#include <math.h> /* For INFINITY and NAN and isnan() */
+
+
+/* Public function. See qcbor_encode_tests.h */
+int32_t CDETest(void)
+{
+ QCBOREncodeContext EC;
+ UsefulBufC Encoded;
+ QCBORError uExpectedErr;
+
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_SerializationCDE(&EC);
+
+ /* Items added to test sorting and preferred encoding of numbers and floats */
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddFloatToMap(&EC, "k", 1.0f);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 1);
+ QCBOREncode_AddDoubleToMap(&EC, "x", 2.0);
+ QCBOREncode_AddDoubleToMap(&EC, "r", 3.4028234663852886E+38);
+ QCBOREncode_AddDoubleToMap(&EC, "b", NAN);
+ QCBOREncode_AddUndefToMap(&EC, "t"); /* Test because dCBOR disallows */
+
+ QCBOREncode_CloseMap(&EC);
+
+ uExpectedErr = QCBOREncode_Finish(&EC, &Encoded);
+ if(uExpectedErr != QCBOR_SUCCESS) {
+ return 2;
+ }
+
+ static const uint8_t spExpectedCDE[] = {
+ 0xA6, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E,
+ 0x00, 0x61, 0x6B, 0xF9, 0x3C, 0x00, 0x61, 0x72,
+ 0xFA, 0x7F, 0x7F, 0xFF, 0xFF, 0x61, 0x74, 0xF7,
+ 0x61, 0x78, 0xF9, 0x40, 0x00};
+
+ if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedCDE),
+ Encoded)) {
+ return 1;
+ }
+
+
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ uExpectedErr = QCBOR_ERR_NOT_PREFERRED;
+#else
+ uExpectedErr = QCBOR_SUCCESS;
+#endif
+
+ /* Next, make sure methods that encode non-CDE error out */
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_SerializationCDE(&EC);
+ QCBOREncode_OpenMapIndefiniteLength(&EC);
+ QCBOREncode_CloseMap(&EC);
+ if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
+ return 100;
+ }
+
+ return 0;
+}
+
+/* Public function. See qcbor_encode_tests.h */
+int32_t DCBORTest(void)
+{
+ QCBOREncodeContext EC;
+ UsefulBufC Encoded;
+ QCBORError uExpectedErr;
+
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_SerializationdCBOR(&EC);
+
+ /* Items added to test sorting and preferred encoding of numbers and floats */
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddFloatToMap(&EC, "k", 1.0f);
+ QCBOREncode_AddInt64ToMap(&EC, "a", 1);
+ QCBOREncode_AddDoubleToMap(&EC, "x", 2.0);
+ QCBOREncode_AddDoubleToMap(&EC, "r", 3.4028234663852886E+38);
+ QCBOREncode_AddDoubleToMap(&EC, "b", NAN);
+
+ QCBOREncode_CloseMap(&EC);
+
+ 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};
+
+ if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpecteddCBOR),
+ Encoded)) {
+ return 1;
+ }
+
+
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ uExpectedErr = QCBOR_ERR_NOT_PREFERRED;
+#else
+ uExpectedErr = QCBOR_SUCCESS;
+#endif
+
+ /* Next, make sure methods that encode of non-CDE error out */
+
+ /* Indefinite-length map */
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_SerializationdCBOR(&EC);
+ QCBOREncode_OpenMapIndefiniteLength(&EC);
+ QCBOREncode_CloseMap(&EC);
+ if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
+ return 100;
+ }
+
+ /* Indefinite-length array */
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_SerializationdCBOR(&EC);
+ QCBOREncode_OpenArrayIndefiniteLength(&EC);
+ QCBOREncode_CloseMap(&EC);
+ if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
+ return 101;
+ }
+
+ /* The "undef" special value */
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_SerializationdCBOR(&EC);
+ QCBOREncode_AddUndef(&EC);
+ QCBOREncode_CloseMap(&EC);
+ if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
+ 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. */
+
+ return 0;
+
+}
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index 8452009..5e2f978 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2022, Laurence Lundblade.
+ Copyright (c) 2018-2024, Laurence Lundblade.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -191,5 +191,17 @@
int32_t OpenCloseBytesTest(void);
+/* Test map sorting */
+int32_t SortMapTest(void);
+
+#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+
+/* Test CBOR Deterministic Encoding */
+int32_t CDETest(void);
+
+/* Test "dCBOR" mode */
+int32_t DCBORTest(void);
+
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
#endif /* defined(__QCBOR__qcbor_encode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index d1d49ed..e24506a 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -61,108 +61,127 @@
TEST_ENTRY(UBMacroConversionsTest),
TEST_ENTRY(UBUtilTests),
TEST_ENTRY(UIBTest_IntegerFormat),
- TEST_ENTRY(UBAdvanceTest)
+ TEST_ENTRY(UBAdvanceTest),
+ TEST_ENTRY(UOBExtraTests)
};
static test_entry s_tests[] = {
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
- TEST_ENTRY(GetMapAndArrayTest),
- TEST_ENTRY(TellTests),
- TEST_ENTRY(ParseMapAsArrayTest),
+ TEST_ENTRY(GetMapAndArrayTest),
+ TEST_ENTRY(TellTests),
+ TEST_ENTRY(ParseMapAsArrayTest),
TEST_ENTRY(SpiffyDateDecodeTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
- TEST_ENTRY(ErrorHandlingTests),
- TEST_ENTRY(OpenCloseBytesTest),
- TEST_ENTRY(EnterBstrTest),
- TEST_ENTRY(IntegerConvertTest),
- TEST_ENTRY(EnterMapTest),
- TEST_ENTRY(QCBORHeadTest),
- TEST_ENTRY(EmptyMapsAndArraysTest),
- TEST_ENTRY(NotWellFormedTests),
+ TEST_ENTRY(ErrorHandlingTests),
+ TEST_ENTRY(OpenCloseBytesTest),
+ TEST_ENTRY(EnterBstrTest),
+ TEST_ENTRY(IntegerConvertTest),
+ TEST_ENTRY(EnterMapTest),
+ TEST_ENTRY(QCBORHeadTest),
+ TEST_ENTRY(EmptyMapsAndArraysTest),
+ TEST_ENTRY(NotWellFormedTests),
+
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
- TEST_ENTRY(IndefiniteLengthNestTest),
- TEST_ENTRY(IndefiniteLengthArrayMapTest),
- TEST_ENTRY(NestedMapTestIndefLen),
+ TEST_ENTRY(IndefiniteLengthNestTest),
+ TEST_ENTRY(IndefiniteLengthArrayMapTest),
+ TEST_ENTRY(NestedMapTestIndefLen),
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
- TEST_ENTRY(SimpleValueDecodeTests),
- TEST_ENTRY(DecodeFailureTests),
- TEST_ENTRY(EncodeRawTest),
- TEST_ENTRY(RTICResultsTest),
- TEST_ENTRY(MapEncodeTest),
- TEST_ENTRY(ArrayNestingTest1),
- TEST_ENTRY(ArrayNestingTest2),
+
+ TEST_ENTRY(SimpleValueDecodeTests),
+ TEST_ENTRY(DecodeFailureTests),
+ TEST_ENTRY(EncodeRawTest),
+ TEST_ENTRY(RTICResultsTest),
+ TEST_ENTRY(MapEncodeTest),
+ TEST_ENTRY(ArrayNestingTest1),
+ TEST_ENTRY(ArrayNestingTest2),
+
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- TEST_ENTRY(ArrayNestingTest3),
+ TEST_ENTRY(ArrayNestingTest3),
#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
- TEST_ENTRY(EncodeDateTest),
- TEST_ENTRY(SimpleValuesTest1),
- TEST_ENTRY(IntegerValuesTest1),
- TEST_ENTRY(AllAddMethodsTest),
- TEST_ENTRY(ParseTooDeepArrayTest),
- TEST_ENTRY(ComprehensiveInputTest),
+
+ TEST_ENTRY(EncodeDateTest),
+ TEST_ENTRY(SimpleValuesTest1),
+ TEST_ENTRY(IntegerValuesTest1),
+ TEST_ENTRY(AllAddMethodsTest),
+ TEST_ENTRY(ParseTooDeepArrayTest),
+ TEST_ENTRY(ComprehensiveInputTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
- TEST_ENTRY(ParseMapTest),
+ TEST_ENTRY(ParseMapTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
- TEST_ENTRY(BasicEncodeTest),
- TEST_ENTRY(NestedMapTest),
- TEST_ENTRY(BignumParseTest),
+ TEST_ENTRY(BasicEncodeTest),
+ TEST_ENTRY(NestedMapTest),
+ TEST_ENTRY(BignumParseTest),
+
#ifndef QCBOR_DISABLE_TAGS
- TEST_ENTRY(OptTagParseTest),
- TEST_ENTRY(DateParseTest),
- TEST_ENTRY(DecodeTaggedTypeTests),
+ TEST_ENTRY(OptTagParseTest),
+ TEST_ENTRY(DateParseTest),
+ TEST_ENTRY(DecodeTaggedTypeTests),
#endif /* QCBOR_DISABLE_TAGS */
- TEST_ENTRY(ShortBufferParseTest2),
- TEST_ENTRY(ShortBufferParseTest),
- TEST_ENTRY(ParseDeepArrayTest),
- TEST_ENTRY(SimpleArrayTest),
- TEST_ENTRY(IntegerValuesParseTest),
+
+ TEST_ENTRY(ShortBufferParseTest2),
+ TEST_ENTRY(ShortBufferParseTest),
+ TEST_ENTRY(ParseDeepArrayTest),
+ TEST_ENTRY(SimpleArrayTest),
+ TEST_ENTRY(IntegerValuesParseTest),
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
- TEST_ENTRY(AllocAllStringsTest),
- TEST_ENTRY(MemPoolTest),
- TEST_ENTRY(IndefiniteLengthStringTest),
+ TEST_ENTRY(AllocAllStringsTest),
+ TEST_ENTRY(MemPoolTest),
+ TEST_ENTRY(IndefiniteLengthStringTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
- TEST_ENTRY(SpiffyIndefiniteLengthStringsTests),
+ TEST_ENTRY(SpiffyIndefiniteLengthStringsTests),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
- TEST_ENTRY(SetUpAllocatorTest),
- TEST_ENTRY(CBORTestIssue134),
+ TEST_ENTRY(SetUpAllocatorTest),
+ TEST_ENTRY(CBORTestIssue134),
+
#endif /* #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
TEST_ENTRY(FloatValuesTests),
+ TEST_ENTRY(PreciseNumbersTest),
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
- TEST_ENTRY(GeneralFloatEncodeTests),
- TEST_ENTRY(GeneralFloatDecodeTests),
+ TEST_ENTRY(GeneralFloatEncodeTests),
+ TEST_ENTRY(GeneralFloatDecodeTests),
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
- TEST_ENTRY(BstrWrapTest),
- TEST_ENTRY(BstrWrapErrorTest),
- TEST_ENTRY(BstrWrapNestTest),
- TEST_ENTRY(CoseSign1TBSTest),
+
+ TEST_ENTRY(BstrWrapTest),
+ TEST_ENTRY(BstrWrapErrorTest),
+ TEST_ENTRY(BstrWrapNestTest),
+ TEST_ENTRY(CoseSign1TBSTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
- TEST_ENTRY(StringDecoderModeFailTest),
+ TEST_ENTRY(StringDecoderModeFailTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
- TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
- TEST_ENTRY_DISABLED(TooLargeInputTest),
- TEST_ENTRY(EncodeErrorTests),
+ TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
+ TEST_ENTRY_DISABLED(TooLargeInputTest),
+ TEST_ENTRY(EncodeErrorTests),
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
- TEST_ENTRY(SimpleValuesIndefiniteLengthTest1),
+ TEST_ENTRY(SimpleValuesIndefiniteLengthTest1),
#endif
- TEST_ENTRY(EncodeLengthThirtyoneTest),
- TEST_ENTRY(CBORSequenceDecodeTests),
- TEST_ENTRY(IntToTests),
+ TEST_ENTRY(EncodeLengthThirtyoneTest),
+ TEST_ENTRY(CBORSequenceDecodeTests),
+ TEST_ENTRY(IntToTests),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
- TEST_ENTRY(PeekAndRewindTest),
+ TEST_ENTRY(PeekAndRewindTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
+
+
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
- TEST_ENTRY(ExponentAndMantissaDecodeTests),
+ TEST_ENTRY(ExponentAndMantissaDecodeTests),
#ifndef QCBOR_DISABLE_TAGS
- TEST_ENTRY(ExponentAndMantissaDecodeFailTests),
+ TEST_ENTRY(ExponentAndMantissaDecodeFailTests),
#endif /* QCBOR_DISABLE_TAGS */
- TEST_ENTRY(ExponentAndMantissaEncodeTests),
+ TEST_ENTRY(ExponentAndMantissaEncodeTests),
#endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
- TEST_ENTRY(ParseEmptyMapInMapTest),
- TEST_ENTRY(BoolTest)
+ TEST_ENTRY(BoolTest),
+ TEST_ENTRY(SortMapTest),
+#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+ TEST_ENTRY(CDETest),
+ TEST_ENTRY(DCBORTest),
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ TEST_ENTRY(ParseEmptyMapInMapTest),
+
};