From 988684a57fa4fb9a1995f482f29c39da6845b2fd Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 4 Feb 2025 23:06:52 -0800 Subject: [PATCH] CBOR-to-JSON: print integers with full precision Instead of performing a lossy conversion to double and printing that. It's irrelevant whether the parser on the other side can store this precision, only that it can parse this. That includes numbers outside the range of int64_t, which CBOR does support. We do this by simply removing code from cbortojson.c and instead just relying on what cborpretty.c already has. Signed-off-by: Thiago Macieira --- src/cbortojson.c | 40 +++++-------------------------------- tests/tojson/tst_tojson.cpp | 21 +++++++++---------- 2 files changed, 14 insertions(+), 47 deletions(-) diff --git a/src/cbortojson.c b/src/cbortojson.c index c48fd4d2..9a1cccfd 100644 --- a/src/cbortojson.c +++ b/src/cbortojson.c @@ -644,28 +644,11 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ return CborNoError; } - case CborIntegerType: { - double num; /* JS numbers are IEEE double precision */ - uint64_t val; - cbor_value_get_raw_integer(it, &val); /* can't fail */ - num = (double)val; - - if (cbor_value_is_negative_integer(it)) { - num = -num - 1; /* convert to negative */ - if ((uint64_t)(-num - 1) != val) { - status->flags = NumberPrecisionWasLost | NumberWasNegative; - status->originalNumber = val; - } - } else { - if ((uint64_t)num != val) { - status->flags = NumberPrecisionWasLost; - status->originalNumber = val; - } - } - if (fprintf(out, "%.0f", num) < 0) /* this number has no fraction, so no decimal points please */ - return CborErrorIO; - break; - } + case CborIntegerType: + case CborNullType: + case CborBooleanType: + /* just use cborpretty.c */ + return cbor_value_to_pretty_advance(out, it); case CborByteStringType: case CborTextStringType: { @@ -696,25 +679,12 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ break; } - case CborNullType: - if (fprintf(out, "null") < 0) - return CborErrorIO; - break; - case CborUndefinedType: status->flags = TypeWasNotNative; if (fprintf(out, "\"undefined\"") < 0) return CborErrorIO; break; - case CborBooleanType: { - bool val; - cbor_value_get_boolean(it, &val); /* can't fail */ - if (fprintf(out, val ? "true" : "false") < 0) - return CborErrorIO; - break; - } - #ifndef CBOR_NO_FLOATING_POINT case CborDoubleType: { double val; diff --git a/tests/tojson/tst_tojson.cpp b/tests/tojson/tst_tojson.cpp index e1e7a76e..d0eb90b8 100644 --- a/tests/tojson/tst_tojson.cpp +++ b/tests/tojson/tst_tojson.cpp @@ -100,13 +100,20 @@ void addFixedData() QTest::newRow("0") << raw("\x00") << "0"; QTest::newRow("1") << raw("\x01") << "1"; QTest::newRow("2^53-1") << raw("\x1b\0\x1f\xff\xff""\xff\xff\xff\xff") << "9007199254740991"; - QTest::newRow("2^64-epsilon") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xf8\x00") << "18446744073709549568"; + QTest::newRow("2^53+1") << raw("\x1b\0\x20\0\0""\0\0\0\1") << "9007199254740993"; + QTest::newRow("2^63-1") << raw("\x1b\x7f\xff\xff\xff""\xff\xff\xff\xff") << "9223372036854775807"; + QTest::newRow("2^64-1") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xff\xff") << "18446744073709551615"; // negative integers QTest::newRow("-1") << raw("\x20") << "-1"; QTest::newRow("-2") << raw("\x21") << "-2"; QTest::newRow("-2^53+1") << raw("\x3b\0\x1f\xff\xff""\xff\xff\xff\xfe") << "-9007199254740991"; - QTest::newRow("-2^64+epsilon") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xf8\x00") << "-18446744073709549568"; + QTest::newRow("-2^53-1") << raw("\x3b\0\x20\0\0""\0\0\0\0") << "-9007199254740993"; + QTest::newRow("-2^63+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe") << "-9223372036854775807"; + QTest::newRow("-2^63") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") << "-9223372036854775808"; + QTest::newRow("-2^63-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << "-9223372036854775809"; + QTest::newRow("-2^64+1") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xff\xfe") << "-18446744073709551615"; + QTest::newRow("-2^64") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xff\xff") << "-18446744073709551616"; QTest::newRow("false") << raw("\xf4") << "false"; QTest::newRow("true") << raw("\xf5") << "true"; @@ -618,16 +625,6 @@ void tst_ToJson::metaData_data() QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "\"t\":251"; QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "\"t\":251"; - // integers that are too precise for double - QTest::newRow("2^53+1") << raw("\x1b\0\x20\0\0""\0\0\0\1") - << "\"t\":0,\"v\":\"+20000000000001\""; - QTest::newRow("INT64_MAX-1") << raw("\x1b\x7f\xff\xff\xff""\xff\xff\xff\xfe") - << "\"t\":0,\"v\":\"+7ffffffffffffffe\""; - QTest::newRow("INT64_MAX+1") << raw("\x1b\x80\0\0\0""\0\0\0\1") - << "\"t\":0,\"v\":\"+8000000000000001\""; - QTest::newRow("-2^53-1") << raw("\x3b\0\x20\0\0""\0\0\0\0") - << "\"t\":0,\"v\":\"-20000000000000\""; - // simple values QTest::newRow("simple0") << raw("\xe0") << "\"t\":224,\"v\":0"; QTest::newRow("simple19") << raw("\xf3") << "\"t\":224,\"v\":19";