From 9e7f11a8478d0cf61945ba00db03048d47730ba4 Mon Sep 17 00:00:00 2001 From: Jason Choy Date: Sun, 9 Jun 2013 15:14:47 +0100 Subject: [PATCH 1/5] Implemented a decode option to only decode numbers to reals and never integers --- src/jansson.h | 1 + src/load.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/jansson.h b/src/jansson.h index 77cc5ba..3661e33 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -239,6 +239,7 @@ json_t *json_deep_copy(json_t *value); #define JSON_REJECT_DUPLICATES 0x1 #define JSON_DISABLE_EOF_CHECK 0x2 #define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_NO_INT 0x8 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 1eb3be4..467d067 100644 --- a/src/load.c +++ b/src/load.c @@ -783,7 +783,11 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) } case TOKEN_INTEGER: { - json = json_integer(lex->value.integer); + if (flags & JSON_DECODE_NO_INT) { + json = json_real(lex->value.integer); + } else { + json = json_integer(lex->value.integer); + } break; } From e6bd0aba9ddd086ac76d239c6854cc845a89cddd Mon Sep 17 00:00:00 2001 From: Jason Choy Date: Sun, 9 Jun 2013 15:34:30 +0100 Subject: [PATCH 2/5] Added a simple test for the JSON_DECODE_NO_INT option --- test/suites/api/test_load.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 30ba65c..328462f 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -87,6 +87,17 @@ static void decode_any() json_decref(json); } +static void decode_no_int() +{ + json_t *json; + json_error_t error; + + json = json_loads("42", JSON_DECODE_NO_INT | JSON_DECODE_ANY, &error); + if (!json || !json_is_real(json) || json_real_value(json) != 42.0) + fail("json_load decode no int failed - int"); + json_decref(json); +} + static void load_wrong_args() { json_t *json; @@ -132,6 +143,7 @@ static void run_tests() reject_duplicates(); disable_eof_check(); decode_any(); + decode_no_int(); load_wrong_args(); position(); } From 02a3829363badb184a61cc5a6b89b3ef4d380e4b Mon Sep 17 00:00:00 2001 From: Jason Choy Date: Tue, 11 Jun 2013 11:53:35 +0100 Subject: [PATCH 3/5] Renamed flag to JSON_DECODE_INT_AS_REAL and added documentation --- doc/apiref.rst | 8 ++++++++ doc/conformance.rst | 5 ++++- src/jansson.h | 8 ++++---- src/load.c | 2 +- test/suites/api/test_load.c | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/doc/apiref.rst b/doc/apiref.rst index 198c0c2..a868202 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -950,6 +950,14 @@ macros can be ORed together to obtain *flags*. .. versionadded:: 2.1 +``JSON_DECODE_INT_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 as real + values. + + .. versionadded:: 2.4.1 + 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 diff --git a/doc/conformance.rst b/doc/conformance.rst index 6f4c929..19f603d 100644 --- a/doc/conformance.rst +++ b/doc/conformance.rst @@ -38,6 +38,8 @@ strings. Numbers ======= +.. _real-vs-integer: + Real vs. Integer ---------------- @@ -51,7 +53,8 @@ A JSON number is considered to be a real number if its lexical 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). +will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL`` +decoder flag set all numbers are interpreted as real. All other JSON numbers are considered integers. diff --git a/src/jansson.h b/src/jansson.h index 3661e33..d441fcf 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -236,10 +236,10 @@ json_t *json_deep_copy(json_t *value); /* decoding */ -#define JSON_REJECT_DUPLICATES 0x1 -#define JSON_DISABLE_EOF_CHECK 0x2 -#define JSON_DECODE_ANY 0x4 -#define JSON_DECODE_NO_INT 0x8 +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 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 467d067..235c1e2 100644 --- a/src/load.c +++ b/src/load.c @@ -783,7 +783,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) } case TOKEN_INTEGER: { - if (flags & JSON_DECODE_NO_INT) { + if (flags & JSON_DECODE_INT_AS_REAL) { json = json_real(lex->value.integer); } else { json = json_integer(lex->value.integer); diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 328462f..72667cb 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -92,7 +92,7 @@ static void decode_no_int() json_t *json; json_error_t error; - json = json_loads("42", JSON_DECODE_NO_INT | JSON_DECODE_ANY, &error); + json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); if (!json || !json_is_real(json) || json_real_value(json) != 42.0) fail("json_load decode no int failed - int"); json_decref(json); From c3e9725f0270447fcc688a57aa884f5954e860c8 Mon Sep 17 00:00:00 2001 From: Jason Choy Date: Tue, 11 Jun 2013 12:31:41 +0100 Subject: [PATCH 4/5] Changed :versionadded for JSON_DECODE_INT_AS_REAL docs --- doc/apiref.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/apiref.rst b/doc/apiref.rst index a868202..036bb69 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -956,7 +956,7 @@ macros can be ORed together to obtain *flags*. With this flag enabled the decoder interprets all numbers as real values. - .. versionadded:: 2.4.1 + .. versionadded:: 2.5 Each function also takes an optional :type:`json_error_t` parameter that is filled with error information if decoding fails. It's also From 120a90a155b1db062eb984be53381f65a698e0e4 Mon Sep 17 00:00:00 2001 From: Jason Choy Date: Tue, 11 Jun 2013 15:09:08 +0100 Subject: [PATCH 5/5] Using jsonp_strtod instead of casting to double to catch double overflow --- doc/apiref.rst | 4 +++- src/load.c | 7 ++++++- test/suites/api/test_load.c | 17 ++++++++++++++--- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/doc/apiref.rst b/doc/apiref.rst index 036bb69..d92fb3b 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -954,7 +954,9 @@ macros can be ORed together to obtain *flags*. 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 as real - values. + values. Integers that do not have an exact double representation + will silently result in a loss of precision. Integers that cause + a double overflow will cause an error. .. versionadded:: 2.5 diff --git a/src/load.c b/src/load.c index 235c1e2..b671892 100644 --- a/src/load.c +++ b/src/load.c @@ -775,6 +775,7 @@ error: static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) { json_t *json; + double value; switch(lex->token) { case TOKEN_STRING: { @@ -784,7 +785,11 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) case TOKEN_INTEGER: { if (flags & JSON_DECODE_INT_AS_REAL) { - json = json_real(lex->value.integer); + if(jsonp_strtod(&lex->saved_text, &value)) { + error_set(error, lex, "real number overflow"); + return NULL; + } + json = json_real(value); } else { json = json_integer(lex->value.integer); } diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 72667cb..6eddb34 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -87,14 +87,25 @@ static void decode_any() json_decref(json); } -static void decode_no_int() +static void decode_int_as_real() { json_t *json; json_error_t error; + // This number cannot be represented exactly by a double + const char *imprecise = "9007199254740993"; + json_int_t expected = 9007199254740992ll; + json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); if (!json || !json_is_real(json) || json_real_value(json) != 42.0) - fail("json_load decode no int failed - int"); + fail("json_load decode int as real failed - int"); + json_decref(json); + + // Tests that large numbers are handled correctly + json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, + &error); + if (!json || !json_is_real(json) || expected != json_real_value(json)) + fail("json_load decode int as real failed - expected imprecision"); json_decref(json); } @@ -143,7 +154,7 @@ static void run_tests() reject_duplicates(); disable_eof_check(); decode_any(); - decode_no_int(); + decode_int_as_real(); load_wrong_args(); position(); }