Precision-preserving number decode and conversion (#211)
This is part of dCBOR support. It decodes a number (int or float) into the best C representation. It's good for more than just dCBOR.
* Remove more stuff related to QCBOREncode_AddBytesLenOnly
* Commit beginnings so dev can be merged in
* Precision-preserving number decode
* Fix ifdefs for precise number decoding
* blank lines
* blank lines
* 65-bit negs without float HW
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index dc5d070..833b5b8 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -8832,6 +8832,213 @@
}
+#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 */
+
+
int32_t
ErrorHandlingTests(void)
{