diff --git a/doc/apiref.rst b/doc/apiref.rst index 4bfb6879..3d72f948 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -905,7 +905,7 @@ inserted to the object. /* obj is a JSON object */ const char *key; json_t *value; - + void *iter = json_object_iter(obj); while(iter) { @@ -1360,6 +1360,17 @@ macros can be ORed together to obtain *flags*. .. versionadded:: 2.6 +``JSON_DECODE_BIGINT_AS_REAL`` + JSON defines only one number type. Jansson distinguishes between + ints and reals. For more information see :ref:`real-vs-integer`. + With this flag enabled the decoder interprets all numbers that do + not fit in the integer type ``json_int_t`` as real values. The + integers converted to real cannot have an exact double + representation and will silently result in a loss of precision. + Integers that cause a double overflow will cause an error. + + .. versionadded:: 2.15 + Each function also takes an optional :type:`json_error_t` parameter that is filled with error information if decoding fails. It's also updated on success; the number of bytes of input read is written to @@ -1435,9 +1446,9 @@ If no error or position information is needed, you can pass *NULL*. file descriptors (such as SOCK_STREAM). Using this function on a non-stream file descriptor will result in undefined behavior. For non-stream file descriptors, see instead :func:`json_loadb()`. In - addition, please note that this function cannot be used on non-blocking - file descriptors (such as a non-blocking socket). Using this function - on non-blocking file descriptors has a high risk of data loss because + addition, please note that this function cannot be used on non-blocking + file descriptors (such as a non-blocking socket). Using this function + on non-blocking file descriptors has a high risk of data loss because it does not support resuming. This function requires POSIX and fails on all non-POSIX systems. diff --git a/doc/conformance.rst b/doc/conformance.rst index 5556a6b8..d1e94eb7 100644 --- a/doc/conformance.rst +++ b/doc/conformance.rst @@ -49,7 +49,9 @@ representation includes one of ``e``, ``E``, or ``.``; regardless if its actual numeric value is a true integer (e.g., all of ``1E6``, ``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL`` -decoder flag set all numbers are interpreted as real. +decoder flag set all numbers are interpreted as real. With the +``JSON_DECODE_BIGINT_AS_REAL`` decoder flag only numbers that do not +fit in the integer data type ``json_int_t`` are interpreted as real. All other JSON numbers are considered integers. diff --git a/src/jansson.h b/src/jansson.h index 391c85e9..df3f74af 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -360,6 +360,7 @@ json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS((warn_unused_result)); #define JSON_DECODE_ANY 0x4 #define JSON_DECODE_INT_AS_REAL 0x8 #define JSON_ALLOW_NUL 0x10 +#define JSON_DECODE_BIGINT_AS_REAL 0x20 typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); diff --git a/src/load.c b/src/load.c index 8ae7abd1..157e110a 100644 --- a/src/load.c +++ b/src/load.c @@ -503,12 +503,23 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) { errno = 0; intval = json_strtoint(saved_text, &end, 10); if (errno == ERANGE) { - if (intval < 0) - error_set(error, lex, json_error_numeric_overflow, - "too big negative integer"); - else - error_set(error, lex, json_error_numeric_overflow, "too big integer"); - goto out; + if (lex->flags & JSON_DECODE_BIGINT_AS_REAL) { + if (jsonp_strtod(&lex->saved_text, &doubleval)) { + error_set(error, lex, json_error_numeric_overflow, "real number overflow for big integer"); + goto out; + } + + lex->token = TOKEN_REAL; + lex->value.real = doubleval; + return 0; + } else { + if (intval < 0) + error_set(error, lex, json_error_numeric_overflow, + "too big negative integer"); + else + error_set(error, lex, json_error_numeric_overflow, "too big integer"); + goto out; + } } assert(end == saved_text + lex->saved_text.length); diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 1c64b0c8..1339730d 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -145,6 +145,35 @@ static void decode_int_as_real() { json_decref(json); } +static void decode_bigint_as_real() { +#if JSON_INTEGER_IS_LONG_LONG + json_t *json; + json_error_t error; + + json = json_loads("9223372036854775807", JSON_DECODE_BIGINT_AS_REAL | JSON_DECODE_ANY, &error); + if (!json || !json_is_integer(json) || json_integer_value(json) != 9223372036854775807) { + fail("json_load could not decode bigint as real failed - max int64 not an integer"); + } + json_decref(json); + + json = json_loads("9223372036854775808", JSON_DECODE_BIGINT_AS_REAL | JSON_DECODE_ANY, &error); + fprintf(stderr, "%f\n", json_real_value(json)); + /* No rounding error because 9223372036854775808 = 2^63 */ + if (!json || !json_is_real(json) || json_real_value(json) != 9223372036854775808.0) { + fail("json_load could not decode bigint as real failed - max int64 not an integer"); + } + json_decref(json); + + json = json_loads("9223372036854775809", JSON_DECODE_BIGINT_AS_REAL | JSON_DECODE_ANY, &error); + fprintf(stderr, "%f\n", json_real_value(json)); + /* Round down to next fitting binary value which is 2^63 */ + if (!json || !json_is_real(json) || json_real_value(json) != 9223372036854775808.0) { + fail("json_load could not decode bigint as real failed - max int64 not an integer"); + } + json_decref(json); +#endif +} + static void allow_nul() { const char *text = "\"nul byte \\u0000 in string\""; const char *expected = "nul byte \0 in string"; @@ -231,6 +260,7 @@ static void run_tests() { disable_eof_check(); decode_any(); decode_int_as_real(); + decode_bigint_as_real(); allow_nul(); load_wrong_args(); position();