Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions doc/apiref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 3 additions & 1 deletion doc/conformance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions src/jansson.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
23 changes: 17 additions & 6 deletions src/load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
30 changes: 30 additions & 0 deletions test/suites/api/test_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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();
Expand Down