Full big num implementation with preferred serialization and 65-bit negs (#219)
Encoding and decoding of negative big numbers now takes into account the offset of 1 for all CBOR negative numbers. Also, big numbers are encoded with preferred serialization -- when they can be encoded with type 0 and type 1 integers, they are. Encoding and decoding big numbers is no longer a pass through for tagging a binary string.
This is an incompatible change with QCBOR 1.x. A mode configuration call is added to return to the 1.x behavior if desired.
This is because of the realization in work on 65-bit negative numbers that big numbers need preferred serialization.
This affects big floats and decimal fractions when the mantissa is a big number.
New methods to encode big numbers with non-preferred serialization are added.
A new method is added to process a raw big number for the offset of one for negatives. This is outside of spiffy decode. It does a little big number arithmetic.
dCBOR numeric reduction now includes 65-bit integers both for number encoding and decoding.
* Checkpoint work on 65-bit negs
* check point ... mostly working
* Checkpoint work on preferred bignums
* Tests passing...
* bignum encode improved to near completion
* Fix #ifdef fan out and error condition bug
* Code tidiness
* debugging unreplicatable CI failure
* v1 compat mode; EAM encode test fan out starting
* debugging ci...
* hacking ci failures
* CI failure hacking
* CI hacking
* Clean up some left over junk
* more leftover junk
* Clean up test leftovers
* Fixes for #ifdef fan out
* Fix conversion of uint to zero, even though it's never used
* REmove more junk
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/float_tests.c b/test/float_tests.c
index ce078d4..d59241f 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -281,10 +281,10 @@
{"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
{"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x3B\x7F\xFF\xFF\xFF\xFF\xFF\xFB\xFF", 9}},
- /* -18446742974197924000.0.0 -- large negative that converts to float, but too large for int64 */
- {-18446742974197924000.0, -18446742974197924000.0f,
+ /* -18446742974197923840.0 -- large negative that converts to float, but too large for int64 */
+ {-18446742974197923840.0, -18446742974197923840.0f,
{"\xFA\xDF\x7F\xFF\xFF", 5}, {"\xFB\xC3\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
- {"\xFA\xDF\x7F\xFF\xFF", 5}, {"\xFA\xDF\x7F\xFF\xFF", 5}},
+ {"\xFA\xDF\x7F\xFF\xFF", 5}, {"\x3B\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF", 9}},
/* 3.4028234663852886E+38 -- largest possible single */
{3.4028234663852886E+38, 3.40282347E+38f,
@@ -414,7 +414,7 @@
for(uTestIndex = 0; FloatTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
pTestCase = &FloatTestCases[uTestIndex];
- if(uTestIndex == 34) {
+ if(uTestIndex == 40) {
uDecoded = 1;
}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 191c448..202be19 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -79,8 +79,11 @@
return (int32_t)uCode;
}
+
/*
[
+ -18446744073709551616,
+ -18446744073709551615,
-9223372036854775808,
-4294967297,
-4294967296,
@@ -175,13 +178,13 @@
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
return (int32_t)nCBORError;
if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
- Item.val.uint64 != 18446744073709551615ULL)
+ Item.val.uint64 != 0xffffffffffffffff)
return -1;
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
return (int32_t)nCBORError;
if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
- Item.val.uint64 != 18446744073709551614ULL)
+ Item.val.uint64 != 0xfffffffffffffffe)
return -1;
if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
@@ -2558,7 +2561,7 @@
}
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
- if(nIndex == 54) {
+ if(nIndex == 55) {
uCBORError = 9; /* For setting break points */
}
@@ -4648,15 +4651,155 @@
0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
+
+struct BignumDecodeTest {
+ const char *szDescription;
+ UsefulBufC Encoded;
+ QCBORError uErr;
+ UsefulBufC ExpectedBigNum;
+ bool bExpectedSign;
+};
+
#ifndef QCBOR_DISABLE_TAGS
+static struct BignumDecodeTest BignumDecodeTests[] = {
+ {
+ "-18446744073709551617",
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+ true
+ },
+ {
+ "-18446744073709551616 preferred",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ true
+ },
+ {
+ "-18446744073709551616 as big num",
+ {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ true
+ },
+ {
+ "-9223372036854775808 -(2^63)",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ {"\x80\x00\x00\x00\x00\x00\x00\x00", 8},
+ true
+ },
+
+ {
+ "Preferred -1",
+ {"\x20", 1},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ true
+ },
+ {
+ "bignum -1",
+ {"\xc3\x42\x00\x00", 4},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ true
+ },
+ {
+ "bignum -1 empty buffer",
+ {"\xc3\x40", 2},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ true
+ },
+ {
+ "Preferred Zero",
+ {"\x00", 1},
+ QCBOR_SUCCESS,
+ {"\x00", 1},
+ false
+ },
+ {
+ "Bignum zero",
+ {"\xC2\x40", 2},
+ QCBOR_SUCCESS,
+ {"\x00", 1},
+ false
+ },
+ {
+ "Bignum zero with leading zeros",
+ {"\xC2\x43\x00\x00\x00", 5},
+ QCBOR_SUCCESS,
+ {"\x00", 1},
+ false
+ },
+ {
+ "Preferred one",
+ {"\x01", 1},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ false
+ },
+ {
+ "Bignum one",
+ {"\xc2\x41\x01", 3},
+ QCBOR_SUCCESS,
+ {"\x01", 1},
+ false
+ },
+ {
+ "512 with leading zeros",
+ {"\x1A\x00\x00\x02\x00", 5},
+ QCBOR_SUCCESS,
+ {"\x02\x00", 2},
+ false
+ },
+ {
+ "512 with leading zeros again",
+ {"\xc2\x46\x00\x00\x00\x00\x02\x00", 8},
+ QCBOR_SUCCESS,
+ {"\x02\x00", 2},
+ false
+ },
+ {
+ "1297093868730187896 (a byte pattern with zeros and different digits",
+ {"\x1B\x12\x00\x34\x00\x56\x00\x00\x78", 9},
+ QCBOR_SUCCESS,
+ {"\x12\x00\x34\x00\x56\x00\x00\x78", 8},
+ false
+ },
+ {
+ "Preferred UINT64_MAX",
+ {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+ false
+ },
+ {
+ "Bignum UINT64_MAX",
+ {"\xC2\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ QCBOR_SUCCESS,
+ {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+ false
+ },
+ {
+ "UINT64_MAX + 1",
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ QCBOR_SUCCESS,
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ false
+ }
+};
+
+
/* The expected big num */
static const uint8_t spBigNum[] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00};
-#endif /* QCBOR_DISABLE_TAGS */
+#endif /* ! QCBOR_DISABLE_TAGS */
-int32_t BignumParseTest(void)
+int32_t BignumDecodeTest(void)
{
QCBORDecodeContext DCtx;
QCBORItem Item;
@@ -4732,10 +4875,72 @@
UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
return -14;
}
-
-
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+
+ unsigned uTestIndex;
+ unsigned uTestCount;
+ struct BignumDecodeTest *pTest;
+ QCBORError uErr;
+ UsefulBuf_MAKE_STACK_UB( BignumBuf, 200);
+ UsefulBufC ResultBigNum;
+ bool bIsNeg;
+
+ uTestCount = (int)C_ARRAY_COUNT(BignumDecodeTests, struct BignumDecodeTest);
+
+ for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+ pTest = &BignumDecodeTests[uTestIndex];
+
+ if(uTestIndex == 13) {
+ bIsNeg = false; /* Line of code so a break point can be set. */
+ }
+
+ QCBORDecode_Init(&DCtx, pTest->Encoded, 0);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 1, uErr);
+ }
+
+ uErr = QCBORDecode_BignumPreferred(Item, BignumBuf, &ResultBigNum, &bIsNeg);
+ if(uErr != pTest->uErr) {
+ return MakeTestResultCode(uTestIndex, 2, uErr);
+ }
+
+ if(uErr != QCBOR_SUCCESS) {
+ continue; /* This test passed */
+ }
+
+ if(UsefulBuf_Compare(ResultBigNum, pTest->ExpectedBigNum)) {
+ return MakeTestResultCode(uTestIndex, 3, 0);
+ }
+
+ if(bIsNeg != pTest->bExpectedSign) {
+ return MakeTestResultCode(uTestIndex, 4, 0);
+ }
+
+ uErr = QCBORDecode_BignumPreferred(Item, (UsefulBuf){NULL, 200}, &ResultBigNum, &bIsNeg);
+ if(ResultBigNum.len != pTest->ExpectedBigNum.len) {
+ return MakeTestResultCode(uTestIndex, 5, uErr);
+ }
+
+ QCBORDecode_Init(&DCtx, pTest->Encoded, 0);
+ QCBORDecode_GetBigNumPreferred(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, BignumBuf, &ResultBigNum, &bIsNeg);
+ uErr = QCBORDecode_GetError(&DCtx);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 6, uErr);
+ }
+
+ if(UsefulBuf_Compare(ResultBigNum, pTest->ExpectedBigNum)) {
+ return MakeTestResultCode(uTestIndex, 7, 0);
+ }
+
+ if(bIsNeg != pTest->bExpectedSign) {
+ return MakeTestResultCode(uTestIndex, 8, 0);
+ }
+
+ }
+
+
#else
if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_TAGS_DISABLED) {
@@ -7732,6 +7937,10 @@
return (int32_t)(3333+nIndex);
}
+ if(nIndex == 21) {
+ uInt = 99; // For break point only
+ }
+
int64_t nInt;
QCBORDecode_GetInt64ConvertAll(&DCtx, 0xffff, &nInt);
if(QCBORDecode_GetError(&DCtx) != pF->uErrorInt64) {
@@ -10109,7 +10318,7 @@
QCBORDecode_Init(&DCtx,UsefulBuf_FROM_SZ_LITERAL("\xa2\x00\x00\x00\x00"), QCBOR_DECODE_MODE_CDE);
QCBORDecode_EnterMap(&DCtx, &Item);
if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_DUPLICATE_LABEL) {
- return 5000;
+ return -5000;
}
return ProcessDecodeFailures(DecodeConformanceFailures,
@@ -10187,39 +10396,98 @@
{0, UINT64_MAX, 0}
},
{
- "INT64_MIN",
+ "Largest float that is also representable as int64_t",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xfb\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {-9223372036854774784, 0, 0}
+ },
+ {
+ "-9223372036854775807",
+ {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xfe", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_INT64,
+ {-9223372036854775807, 0, 0}
+ },
+ {
+ "Largest representable in int64_t (INT64_MIN)",
{"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
QCBOR_SUCCESS,
QCBOR_TYPE_INT64,
{INT64_MIN, 0, 0}
},
{
+ "-9223372036854775809 First encoded as 65-bit neg",
+ {"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 0, 0}
+ },
+
+ {
+ "-9223372036854777856 First float not representable as int64_t",
+ {"\x3B\x80\x00\x00\x00\x00\x00\x07\xFF", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -9223372036854777856.0}
+ },
+
+ {
"18446742974197923840",
{"\xFB\x43\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
QCBOR_SUCCESS,
QCBOR_TYPE_UINT64,
{0, 18446742974197923840ULL, 0}
},
+
{
- "65-bit neg, too much precision",
- {"\x3B\x80\x00\x00\x00\x00\x00\x00\x01", 9},
- QCBOR_SUCCESS,
- QCBOR_TYPE_65BIT_NEG_INT,
- {0, 0x8000000000000001, 0}
- },
- {
- "65-bit neg lots of precision",
- {"\x3B\xff\xff\xff\xff\xff\xff\xf0\x00", 9},
+ "-18446744073709547522 65-bit neg lots of precision",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xef\xff", 9},
QCBOR_SUCCESS,
QCBOR_TYPE_DOUBLE,
- {0, 0, -18446744073709547521.0}
+ {0, 0, -18446744073709547522.0}
},
+
+ {
+ "-18446744073709549568 Next to largest float encodable as 65-bit neg",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xf7\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709549568.0}
+ },
+
+ {
+ "-18446744073709551616 Largest possible encoded 65-bit neg",
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709551616.0}
+ },
+ {
+ "-18446744073709551617 First value representable only as a tag 3 big num",
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+#ifndef QCBOR_DISABLE_TAGS
+ QCBOR_ERR_UNEXPECTED_TYPE,
+#else
+ QCBOR_ERR_TAGS_DISABLED,
+#endif /* ! QCBOR_DISABLE_TAGS */
+ QCBOR_TYPE_NONE,
+ {0, 0, -0}
+ },
+ {
+ "-18446744073709555712 First whole integer that must be encoded as float in DCBOR",
+ {"\xFB\xC3\xF0\x00\x00\x00\x00\x00\x01", 9},
+ QCBOR_SUCCESS,
+ QCBOR_TYPE_DOUBLE,
+ {0, 0, -18446744073709555712.0}
+ },
+
{
"65-bit neg very precise",
{"\x3B\xff\xff\xff\xff\xff\xff\xf8\x00", 9},
QCBOR_SUCCESS,
- QCBOR_TYPE_DOUBLE,
- {0, 0, -18446744073709549569.0}
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 18446744073709549568ULL, 0.0}
},
{
"65-bit neg too precise",
@@ -10232,8 +10500,8 @@
"65-bit neg, power of two",
{"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 9},
QCBOR_SUCCESS,
- QCBOR_TYPE_DOUBLE,
- {0, 0, -9223372036854775809.0}
+ QCBOR_TYPE_65BIT_NEG_INT,
+ {0, 9223372036854775808ULL, 0.0}
},
{
"Zero",
@@ -10260,20 +10528,20 @@
int32_t
-PreciseNumbersTest(void)
+PreciseNumbersDecodeTest(void)
{
- int i;
+ unsigned uTestIndex;
+ unsigned uTestCount;
QCBORError uErr;
QCBORItem Item;
QCBORDecodeContext DCtx;
const struct PreciseNumberConversion *pTest;
- const int count = (int)C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+ uTestCount = C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+ for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+ pTest = &PreciseNumberConversions[uTestIndex];
- for(i = 0; i < count; i++) {
- pTest = &PreciseNumberConversions[i];
-
- if(i == 11) {
+ if(uTestIndex == 18) {
uErr = 99; // For break point only
}
@@ -10284,11 +10552,11 @@
uErr = QCBORDecode_GetError(&DCtx);
if(uErr != pTest->uError) {
- return i * 1000 + (int)uErr;
+ return MakeTestResultCode(uTestIndex, 1, uErr);
}
if(pTest->qcborType != Item.uDataType) {
- return i * 1000 + 200;
+ return MakeTestResultCode(uTestIndex, 2, 0);
}
if(pTest->qcborType == QCBOR_TYPE_NONE) {
@@ -10298,25 +10566,25 @@
switch(pTest->qcborType) {
case QCBOR_TYPE_INT64:
if(Item.val.int64 != pTest->number.int64) {
- return i * 1000 + 300;
+ return MakeTestResultCode(uTestIndex, 3, 0);
}
break;
case QCBOR_TYPE_UINT64:
- case QCBOR_TYPE_65BIT_NEG_INT:
+ case QCBOR_TYPE_NEGBIGNUM:
if(Item.val.uint64 != pTest->number.uint64) {
- return i * 1000 + 400;
+ return MakeTestResultCode(uTestIndex, 4, 0);
}
break;
case QCBOR_TYPE_DOUBLE:
if(isnan(pTest->number.d)) {
if(!isnan(Item.val.dfnum)) {
- return i * 1000 + 600;
+ return MakeTestResultCode(uTestIndex, 5, 0);
}
} else {
if(Item.val.dfnum != pTest->number.d) {
- return i * 1000 + 500;
+ return MakeTestResultCode(uTestIndex, 6, 0);
}
}
break;
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index a6d5837..31d9e48 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -185,7 +185,7 @@
/*
Parse some big numbers, positive and negative
*/
-int32_t BignumParseTest(void);
+int32_t BignumDecodeTest(void);
/*
@@ -324,7 +324,7 @@
*/
int32_t DecodeConformanceTests(void);
-int32_t PreciseNumbersTest(void);
+int32_t PreciseNumbersDecodeTest(void);
int32_t ErrorHandlingTests(void);
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 7645475..88251ac 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -128,6 +128,18 @@
}
+static inline int32_t
+MakeTestResultCode(uint32_t uTestCase,
+ uint32_t uTestNumber,
+ QCBORError uErrorCode)
+{
+ uint32_t uCode = (uTestCase * 1000000) +
+ (uTestNumber * 1000) +
+ (uint32_t)uErrorCode;
+ return (int32_t)uCode;
+}
+
+
// One big buffer that is used by all the tests to encode into
// Putting it in uninitialized data is better than using a lot
// of stack. The tests should run on small devices too.
@@ -276,10 +288,10 @@
static const uint8_t spExpectedEncodedAll[] = {
- 0x98, 0x24, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
+ 0x98, 0x23, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
0x64, 0x1a, 0x05, 0x5d, 0x23, 0x15, 0x65, 0x49, 0x4e, 0x54,
0x36, 0x34, 0xd8, 0x4c, 0x1b, 0x00, 0x00, 0x00, 0x12, 0x16,
- 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0x20, 0xa4, 0x63, 0x4c, 0x42,
+ 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0xa4, 0x63, 0x4c, 0x42,
0x4c, 0x18, 0x4d, 0x23, 0x18, 0x58, 0x78, 0x1a, 0x4e, 0x45,
0x47, 0x4c, 0x42, 0x4c, 0x54, 0x48, 0x41, 0x54, 0x20, 0x49,
0x53, 0x20, 0x4b, 0x49, 0x4e, 0x44, 0x20, 0x4f, 0x46, 0x20,
@@ -476,11 +488,11 @@
0x71, 0x47, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x20, 0x69, 0x73,
0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0xf5, 0x19,
0x10, 0x41, 0xf5, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x63, 0x42, 0x4E, 0x2B,
0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
+ 0x01, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
0x3F, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
@@ -523,7 +535,6 @@
QCBOREncode_AddInt64(pECtx, 77689989909);
QCBOREncode_AddUInt64(pECtx, 0);
QCBOREncode_AddInt64(pECtx, -44);
- QCBOREncode_AddNegativeUInt64(pECtx, 0);
/* ints that go in maps */
QCBOREncode_OpenMap(pECtx);
@@ -672,7 +683,7 @@
QCBOREncode_CloseMap(pECtx);
/* Big numbers */
- static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
const UsefulBufC BIGNUM = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBignum);
QCBOREncode_AddPositiveBignum(pECtx, BIGNUM);
QCBOREncode_AddNegativeBignum(pECtx, BIGNUM);
@@ -736,13 +747,6 @@
#else
uExpectedErr = QCBOR_SUCCESS;
#endif
- /* Test the QCBOR_ERR_NOT_ALLOWED error codes */
- QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
- QCBOREncode_AddNegativeUInt64(&ECtx, 1);
- if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
- nReturn = -21;
- goto Done;
- }
#if !defined(QCBOR_DISABLE_ENCODE_USAGE_GUARDS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
@@ -944,6 +948,131 @@
return(nReturn);
}
+struct BigNumEncodeTest {
+ const char *szDescription;
+ UsefulBufC BigNum;
+ /* Expect all to succeed; no special error codes needed */
+ UsefulBufC PositiveNoPreferred;
+ UsefulBufC PositivePreferred;
+ UsefulBufC NegativeNoPreferred;
+ UsefulBufC NegativePreferred;
+};
+
+struct BigNumEncodeTest BigNumEncodeTestCases[] = {
+ {
+ "2^96 -1 or 79228162514264337593543950335 pos and neg with leading zeros",
+ {"\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 15},
+ {"\xC2\x4F\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 17},
+ {"\xC2\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 14},
+ {"\xC3\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", 14},
+ {"\xC3\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", 14},
+ },
+
+ {
+ "2^64+1 or 18446744073709551617 pos and neg)",
+ {"\x01\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x01", 11},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x01", 11},
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ },
+ {
+ "2^64 or 18446744073709551616 pos and neg)",
+ {"\x01\x00\x00\x00\x0000\x00\x00\x00", 9},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+ {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ },
+ {
+ "2^64 - 1 or 18446744073709551615 pos and neg",
+ {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+ {"\xC2\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+ {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xfe", 10},
+ {"\x3B\xff\xff\xff\xff\xff\xff\xff\xfe", 9},
+ },
+ {
+ "1 and -1",
+ {"\x01", 1},
+ {"\xC2\x41\x01", 3},
+ {"\x01", 1},
+ {"\xC3\x41\x00", 3},
+ {"\x20", 1},
+ },
+ {
+ "0 and error",
+ {"\x00", 1},
+ {"\xC2\x41\x00", 3},
+ {"\x00", 1},
+ NULLUsefulBufC,
+ NULLUsefulBufC,
+ },
+ {
+ "leading zeros -- 0 and error",
+ {"\x00\x00\x00\x00", 4},
+ {"\xC2\x44\x00\x00\x00\x00", 6},
+ {"\x00", 1},
+ NULLUsefulBufC,
+ NULLUsefulBufC,
+ }
+
+};
+
+
+int32_t BigNumEncodeTests(void)
+{
+ unsigned uTestIndex;
+ unsigned uTestCount;
+ QCBOREncodeContext Enc;
+ UsefulBufC B;
+ const struct BigNumEncodeTest *pTest;
+
+ uTestCount = (int)C_ARRAY_COUNT(BigNumEncodeTestCases, struct BigNumEncodeTest);
+
+ for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+ pTest = &BigNumEncodeTestCases[uTestIndex];
+
+ if(uTestIndex == 3) {
+ B.len = 0; /* Line of code so a break point can be set. */
+ }
+
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddTPositiveBignumNoPreferred(&Enc, QCBOR_ENCODE_AS_TAG, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->PositiveNoPreferred)) {
+ return MakeTestResultCode(uTestIndex, 1, QCBOR_SUCCESS);
+ }
+
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddPositiveBignum (&Enc, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->PositivePreferred)) {
+ return MakeTestResultCode(uTestIndex, 2, QCBOR_SUCCESS);
+ }
+
+ if(!UsefulBuf_IsNULLC(pTest->NegativeNoPreferred)){
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddTNegativeBignumNoPreferred(&Enc, QCBOR_ENCODE_AS_TAG, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->NegativeNoPreferred)) {
+ return MakeTestResultCode(uTestIndex, 3, QCBOR_SUCCESS);
+ }
+ }
+
+ if(!UsefulBuf_IsNULLC(pTest->NegativePreferred)){
+ QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ QCBOREncode_AddNegativeBignum(&Enc, pTest->BigNum);
+ QCBOREncode_Finish(&Enc, &B);
+ if(UsefulBuf_Compare(B, pTest->NegativePreferred)) {
+ return MakeTestResultCode(uTestIndex, 4, QCBOR_SUCCESS);
+ }
+ }
+ }
+
+ return 0;
+}
+
/*
85 # array(5)
@@ -2765,12 +2894,12 @@
0x07, 0x08, 0x09, 0x10, 0xC4, 0x82, 0x1B, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3,
0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x10, 0xC5, 0x82, 0x19, 0x01, 0x2C,
+ 0x08, 0x09, 0x0F, 0xC5, 0x82, 0x19, 0x01, 0x2C,
0x18, 0x64, 0xC5, 0x82, 0x33, 0xC2, 0x4A, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02,
- 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F};
/*
@@ -2809,10 +2938,10 @@
0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0xC4, 0x82,
0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x10, 0x19, 0x01, 0xF4,
+ 0x06, 0x07, 0x08, 0x09, 0x0F, 0x19, 0x01, 0xF4,
0xC4, 0x82, 0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03,
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x69,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F, 0x69,
0x62, 0x69, 0x67, 0x20, 0x66, 0x6C, 0x6F, 0x61,
0x74, 0xC5, 0x82, 0x19, 0x01, 0x2C, 0x18, 0x64,
0x19, 0x02, 0x58, 0xC5, 0x82, 0x19, 0x01, 0x2C,
@@ -2829,18 +2958,153 @@
0x20, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76,
0x65, 0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02,
- 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F,
0x19, 0x03, 0x20, 0xC5, 0x82, 0x3B, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x10
+ 0x09, 0x0F
};
-int32_t ExponentAndMantissaEncodeTests(void)
+struct EAMEncodeTest {
+ const char *szDescription;
+ int64_t nExponent;
+ UsefulBufC BigNumMantissa;
+ int64_t nMantissa;
+ bool bSign;
+ bool bv1Mode;
+ enum {EAM_Any, EAM_Pref, EAM_CDE} eSerialization;
+ // TODO: add tag requirement
+
+ /* Only testing successes (right?) */
+ UsefulBufC BigFloat;
+ UsefulBufC DecFrac;
+ UsefulBufC BigFloatBig;
+ UsefulBufC DecFracBig;
+};
+
+struct EAMEncodeTest EET[] = {
+ { "basic",
+ -1,
+ NULLUsefulBufC,
+ 3,
+ false,
+ false,
+ EAM_Pref,
+
+ {"\xC5\x82\x20\x03", 4},
+ {"\xC4\x82\x20\x03", 4},
+ NULLUsefulBufC,
+ NULLUsefulBufC
+ },
+
+ { "bignum gets preferred",
+ -1,
+ {"\x00\x03",2},
+ 0,
+ false,
+ false,
+ EAM_Pref,
+
+ NULLUsefulBufC,
+ NULLUsefulBufC,
+ {"\xC5\x82\x20\x03", 4},
+ {"\xC4\x82\x20\x03", 4},
+ }
+
+ // TODO: add more test cases, including converting some of the already-existing
+};
+
+
+
+static void
+EAMTestSetup(const struct EAMEncodeTest *pTest, QCBOREncodeContext *pEnc)
+{
+ QCBOREncode_Init(pEnc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+ if(pTest->bv1Mode) {
+ QCBOREncode_Setv1Compatibility(pEnc);
+ }
+
+ switch(pTest->eSerialization) {
+ case EAM_Pref:
+ QCBOREncode_SerializationPreferred(pEnc);
+ break;
+ case EAM_CDE:
+ QCBOREncode_SerializationCDE(pEnc);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+int32_t
+ExponentAndMantissaEncodeTests(void)
{
QCBOREncodeContext EC;
UsefulBufC EncodedExponentAndMantissa;
+ int nIndex;
+ QCBORError uErr;
+
+ const int nNumberOfTests = C_ARRAY_COUNT(EET, struct EAMEncodeTest);
+
+ for(nIndex = 0; nIndex < nNumberOfTests; nIndex++) {
+ struct EAMEncodeTest *pTest = &EET[nIndex];
+
+
+ if(UsefulBuf_IsNULLC(pTest->BigNumMantissa)) {
+ EAMTestSetup(pTest, &EC);
+
+ QCBOREncode_AddDecimalFraction(&EC, pTest->nMantissa, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 1, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->DecFrac)) {
+ return MakeTestResultCode((uint32_t)nIndex, 2, 0);
+ }
+
+ EAMTestSetup(pTest, &EC);
+ QCBOREncode_AddBigFloat(&EC, pTest->nMantissa, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->BigFloat)) {
+ return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+ }
+
+ } else {
+ EAMTestSetup(pTest, &EC);
+
+ QCBOREncode_AddDecimalFractionBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->DecFracBig)) {
+ return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+ }
+
+ EAMTestSetup(pTest, &EC);
+
+ QCBOREncode_AddBigFloatBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
+ uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+ if(uErr) {
+ return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+ }
+
+ if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->BigFloatBig)) {
+ return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+ }
+ }
+ }
+
// Constant for the big number used in all the tests.
static const uint8_t spBigNum[] = {0x01, 0x02, 0x03, 0x04, 0x05,
@@ -2861,9 +3125,11 @@
return -2;
}
+ struct UBCompareDiagnostic Foo;
+
int nReturn = UsefulBuf_CompareWithDiagnostic(EncodedExponentAndMantissa,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentAndMantissaArray),
- NULL);
+ &Foo);
if(nReturn) {
return nReturn;
}
@@ -2933,9 +3199,7 @@
return -3;
}
-
struct UBCompareDiagnostic Diag;
-
nReturn = UsefulBuf_CompareWithDiagnostic(EncodedExponentAndMantissa,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentAndMantissaMap),
&Diag);
@@ -3650,6 +3914,9 @@
QCBOREncode_AddInt64ToMap(&EC, "a", 1);
QCBOREncode_AddDoubleToMap(&EC, "x", 2.0);
QCBOREncode_AddDoubleToMap(&EC, "r", 3.4028234663852886E+38);
+ QCBOREncode_AddDoubleToMap(&EC, "d1", -18446744073709549568.0);
+ QCBOREncode_AddDoubleToMap(&EC, "d2", -18446744073709551616.0);
+ QCBOREncode_AddDoubleToMap(&EC, "d3", -18446744073709555712.0);
QCBOREncode_AddDoubleToMap(&EC, "b", NAN);
QCBOREncode_CloseMap(&EC);
@@ -3657,9 +3924,7 @@
QCBOREncode_Finish(&EC, &Encoded);
static const uint8_t spExpecteddCBOR[] = {
- 0xA5, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E,
- 0x00, 0x61, 0x6B, 0x01, 0x61, 0x72, 0xFA, 0x7F,
- 0x7F, 0xFF, 0xFF, 0x61, 0x78, 0x02};
+ 0xA8, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E, 0x00, 0x61, 0x6B, 0x01, 0x61, 0x72, 0xFA, 0x7F, 0x7F, 0xFF, 0xFF, 0x61, 0x78, 0x02, 0x62, 0x64, 0x31, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0x62, 0x64, 0x32, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x62, 0x64, 0x33, 0xFB, 0xC3, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpecteddCBOR),
Encoded)) {
@@ -3704,13 +3969,6 @@
return 102;
}
- /* 65-bit negative integers */
- QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
- QCBOREncode_SerializationdCBOR(&EC);
- QCBOREncode_AddNegativeUInt64(&EC, 1);
- if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
- return 103;
- }
/* Improvement: when indefinite length string encoding is supported
* test it here too. */
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index 5e2f978..1be5635 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -94,9 +94,9 @@
/*
- Encodes a goodly number of floats and doubles and checks encoding is right
+ * Big number encoding tests.
*/
-int32_t FloatValuesTest1(void);
+int32_t BigNumEncodeTests(void);
/*
diff --git a/test/run_tests.c b/test/run_tests.c
index 3fe4905..682d5e1 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -67,6 +67,7 @@
static test_entry s_tests[] = {
+ TEST_ENTRY(BigNumEncodeTests),
#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
TEST_ENTRY(DecodeConformanceTests),
#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
@@ -81,8 +82,6 @@
#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
TEST_ENTRY(SpiffyDateDecodeTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
- TEST_ENTRY(ErrorHandlingTests),
- TEST_ENTRY(OpenCloseBytesTest),
TEST_ENTRY(EnterBstrTest),
TEST_ENTRY(IntegerConvertTest),
TEST_ENTRY(EnterMapTest),
@@ -114,7 +113,7 @@
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY(BasicEncodeTest),
TEST_ENTRY(NestedMapTest),
- TEST_ENTRY(BignumParseTest),
+ TEST_ENTRY(BignumDecodeTest),
#ifndef QCBOR_DISABLE_TAGS
TEST_ENTRY(OptTagParseTest),
@@ -142,7 +141,7 @@
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
TEST_ENTRY(FloatValuesTests),
- TEST_ENTRY(PreciseNumbersTest),
+ TEST_ENTRY(PreciseNumbersDecodeTest),
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
TEST_ENTRY(GeneralFloatEncodeTests),
TEST_ENTRY(GeneralFloatDecodeTests),