From ffbab6fedd86ee1a0c7884d62fa7233ce9d745d2 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Fri, 13 Aug 2010 22:06:01 +0300 Subject: [PATCH] Change the underlying type of JSON integer from long to json_int_t json_int_t is typedef'd to long long if it's supported, or long otherwise. There's also some supporting things, like the JSON_INTEGER_FORMAT macro that expands to the printf() conversion specifier that corresponds to json_int_t's actual type. This is a backwards incompatible change. --- configure.ac | 7 ++++ doc/apiref.rst | 41 ++++++++++++++++++---- doc/conformance.rst | 26 +++++++------- src/dump.c | 4 ++- src/jansson.h | 14 ++++++-- src/jansson_config.h.in | 22 ++++++++++++ src/jansson_private.h | 2 +- src/load.c | 27 ++++++++------ src/value.c | 6 ++-- .../invalid-strip/too-big-negative-integer/error | 2 +- .../invalid-strip/too-big-negative-integer/input | 2 +- .../invalid-strip/too-big-positive-integer/error | 2 +- .../invalid-strip/too-big-positive-integer/input | 2 +- test/suites/invalid/too-big-negative-integer/error | 2 +- test/suites/invalid/too-big-negative-integer/input | 2 +- test/suites/invalid/too-big-positive-integer/error | 2 +- test/suites/invalid/too-big-positive-integer/input | 2 +- 17 files changed, 120 insertions(+), 45 deletions(-) diff --git a/configure.ac b/configure.ac index 0b7e79d..54a307c 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,13 @@ AC_PROG_LIBTOOL # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_INT32_T +AC_TYPE_LONG_LONG_INT +case $ac_cv_type_long_long_int in + yes) json_have_long_long=1;; + *) json_have_long_long=0;; +esac +AC_SUBST([json_have_long_long]) + AC_C_INLINE case $ac_cv_c_inline in yes) json_inline=inline;; diff --git a/doc/apiref.rst b/doc/apiref.rst index de2bcfe..91de0c8 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -210,10 +210,10 @@ the user to avoid them. If a circular reference is created, the memory consumed by the values cannot be freed by :cfunc:`json_decref()`. The reference counts never -drops to zero because the values are keeping the circular reference to -themselves. Moreover, trying to encode the values with any of the -encoding functions will fail. The encoder detects circular references -and returns an error status. +drops to zero because the values are keeping the references to each +other. Moreover, trying to encode the values with any of the encoding +functions will fail. The encoder detects circular references and +returns an error status. True, False and Null @@ -287,18 +287,45 @@ String Number ====== -.. cfunction:: json_t *json_integer(long value) +.. ctype:: json_int_t + + This is the C type that is used to store JSON integer values. It + represents the widest integer type available on your system. In + practice it's just a typedef of ``long long`` if your compiler + supports it, otherwise ``long``. + + Usually, you can safely use plain ``int`` in place of + ``json_int_t``, and the implicit C integer conversion handles the + rest. Only when you know that you need a full 64-bit range, you + should use ``json_int_t`` explicitly. + +``JSON_INTEGER_FORMAT`` + + This is a macro that expands to a :cfunc:`printf()` conversion + specifier that corresponds to :ctype:`json_int_t`, without the + leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro + is required because the actual type of :ctype:`json_int_t` can be + either ``long`` or ``long long``, and :cfunc:`printf()` reuiqres + different length modifiers for the two. + + Example:: + + json_int_t x = 123123123; + printf("x is %" JSON_INTEGER_FORMAT "\n", x); + + +.. cfunction:: json_t *json_integer(json_int_t value) .. refcounting:: new Returns a new JSON integer, or *NULL* on error. -.. cfunction:: long json_integer_value(const json_t *integer) +.. cfunction:: json_int_t json_integer_value(const json_t *integer) Returns the associated value of *integer*, or 0 if *json* is not a JSON integer. -.. cfunction:: int json_integer_set(const json_t *integer, long value) +.. cfunction:: int json_integer_set(const json_t *integer, json_int_t value) Sets the associated value of *integer* to *value*. Returns 0 on success and -1 if *integer* is not a JSON integer. diff --git a/doc/conformance.rst b/doc/conformance.rst index e2f94b1..1cb6f44 100644 --- a/doc/conformance.rst +++ b/doc/conformance.rst @@ -36,7 +36,9 @@ Real vs. Integer JSON makes no distinction between real and integer numbers; Jansson does. Real numbers are mapped to the ``double`` type and integers to -the ``long`` type. +the ``json_int_t`` type, which is a typedef of ``long long`` or +``long``, depending on whether ``long long`` is supported by your +compiler or not. A JSON number is considered to be a real number if its lexical representation includes one of ``e``, ``E``, or ``.``; regardless if @@ -54,19 +56,19 @@ Overflow, Underflow & Precision ------------------------------- Real numbers whose absolute values are too small to be represented in -a C double will be silently estimated with 0.0. Thus, depending on +a C ``double`` will be silently estimated with 0.0. Thus, depending on platform, JSON numbers very close to zero such as 1E-999 may result in 0.0. Real numbers whose absolute values are too large to be represented in -a C ``double`` type will result in an overflow error (a JSON decoding +a C ``double`` will result in an overflow error (a JSON decoding error). Thus, depending on platform, JSON numbers like 1E+999 or -1E+999 may result in a parsing error. Likewise, integer numbers whose absolute values are too large to be -represented in the ``long`` type will result in an overflow error (a -JSON decoding error). Thus, depending on platform, JSON numbers like -1000000000000000 may result in parsing error. +represented in the ``json_int_t`` type (see above) will result in an +overflow error (a JSON decoding error). Thus, depending on platform, +JSON numbers like 1000000000000000 may result in parsing error. Parsing JSON real numbers may result in a loss of precision. As long as overflow does not occur (i.e. a total loss of precision), the @@ -94,9 +96,9 @@ Types ----- No support is provided in Jansson for any C numeric types other than -``long`` and ``double``. This excludes things such as unsigned types, -``long long``, ``long double``, etc. Obviously, shorter types like -``short``, ``int`` and ``float`` are implicitly handled via the -ordinary C type coercion rules (subject to overflow semantics). Also, -no support or hooks are provided for any supplemental "bignum" type -add-on packages. +``json_int_t`` and ``double``. This excludes things such as unsigned +types, ``long double``, etc. Obviously, shorter types like ``short``, +``int``, ``long`` (if ``json_int_t`` is ``long long``) and ``float`` +are implicitly handled via the ordinary C type coercion rules (subject +to overflow semantics). Also, no support or hooks are provided for any +supplemental "bignum" type add-on packages. diff --git a/src/dump.c b/src/dump.c index c55d332..b887515 100644 --- a/src/dump.c +++ b/src/dump.c @@ -185,7 +185,9 @@ static int do_dump(const json_t *json, size_t flags, int depth, char buffer[MAX_INTEGER_STR_LENGTH]; int size; - size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%li", json_integer_value(json)); + size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, + "%" JSON_INTEGER_FORMAT, + json_integer_value(json)); if(size >= MAX_INTEGER_STR_LENGTH) return -1; diff --git a/src/jansson.h b/src/jansson.h index 81235ff..5324e57 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -34,6 +34,14 @@ typedef struct { size_t refcount; } json_t; +#if JSON_INTEGER_IS_LONG_LONG +#define JSON_INTEGER_FORMAT "lld" +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ + #define json_typeof(json) ((json)->type) #define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) #define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY) @@ -52,7 +60,7 @@ json_t *json_object(void); json_t *json_array(void); json_t *json_string(const char *value); json_t *json_string_nocheck(const char *value); -json_t *json_integer(long value); +json_t *json_integer(json_int_t value); json_t *json_real(double value); json_t *json_true(void); json_t *json_false(void); @@ -139,13 +147,13 @@ int json_array_insert(json_t *array, size_t index, json_t *value) } const char *json_string_value(const json_t *string); -long json_integer_value(const json_t *integer); +json_int_t json_integer_value(const json_t *integer); double json_real_value(const json_t *real); double json_number_value(const json_t *json); int json_string_set(json_t *string, const char *value); int json_string_set_nocheck(json_t *string, const char *value); -int json_integer_set(json_t *integer, long value); +int json_integer_set(json_t *integer, json_int_t value); int json_real_set(json_t *real, double value); diff --git a/src/jansson_config.h.in b/src/jansson_config.h.in index 1a9d859..d55d992 100644 --- a/src/jansson_config.h.in +++ b/src/jansson_config.h.in @@ -3,6 +3,18 @@ * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can copy the file and do the + * value substitution by hand. + * + * See below for explanations of each substitution variable. */ #ifndef JANSSON_CONFIG_H @@ -11,7 +23,17 @@ #ifdef __cplusplus #define JSON_INLINE inline #else +/* If your compiler supports the inline keyword, @json_inline@ is + replaced with `inline', otherwise empty. */ #define JSON_INLINE @json_inline@ #endif +/* If your compiler supports the `long long` type, + @json_have_long_long@ is replaced with 1, otherwise with 0. */ +#if @json_have_long_long@ +#define JSON_INTEGER_IS_LONG_LONG 1 +#else +#define JSON_INTEGER_IS_LONG 1 +#endif + #endif diff --git a/src/jansson_private.h b/src/jansson_private.h index a04b6f2..bd80346 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -41,7 +41,7 @@ typedef struct { typedef struct { json_t json; - long value; + json_int_t value; } json_integer_t; #define json_to_object(json_) container_of(json_, json_object_t, json) diff --git a/src/load.c b/src/load.c index f86d2b5..2a360ff 100644 --- a/src/load.c +++ b/src/load.c @@ -52,7 +52,7 @@ typedef struct { int line, column; union { char *string; - long integer; + json_int_t integer; double real; } value; } lex_t; @@ -401,6 +401,12 @@ out: free(lex->value.string); } +#ifdef JSON_INTEGER_IS_LONG_LONG +#define json_strtoint strtoll +#else +#define json_strtoint strtol +#endif + static int lex_scan_number(lex_t *lex, char c, json_error_t *error) { const char *saved_text; @@ -430,23 +436,24 @@ static int lex_scan_number(lex_t *lex, char c, json_error_t *error) } if(c != '.' && c != 'E' && c != 'e') { - long value; + json_int_t value; lex_unget_unsave(lex, c); saved_text = strbuffer_value(&lex->saved_text); - value = strtol(saved_text, &end, 10); - assert(end == saved_text + lex->saved_text.length); - if(value == LONG_MAX && errno == ERANGE) { - error_set(error, lex, "too big integer"); - goto out; - } - else if(value == LONG_MIN && errno == ERANGE) { - error_set(error, lex, "too big negative integer"); + errno = 0; + value = json_strtoint(saved_text, &end, 10); + if(errno == ERANGE) { + if(value < 0) + error_set(error, lex, "too big negative integer"); + else + error_set(error, lex, "too big integer"); goto out; } + assert(end == saved_text + lex->saved_text.length); + lex->token = TOKEN_INTEGER; lex->value.integer = value; return 0; diff --git a/src/value.c b/src/value.c index 9310ea5..0f2ae65 100644 --- a/src/value.c +++ b/src/value.c @@ -725,7 +725,7 @@ static json_t *json_string_copy(json_t *string) /*** integer ***/ -json_t *json_integer(long value) +json_t *json_integer(json_int_t value) { json_integer_t *integer = malloc(sizeof(json_integer_t)); if(!integer) @@ -736,7 +736,7 @@ json_t *json_integer(long value) return &integer->json; } -long json_integer_value(const json_t *json) +json_int_t json_integer_value(const json_t *json) { if(!json_is_integer(json)) return 0; @@ -744,7 +744,7 @@ long json_integer_value(const json_t *json) return json_to_integer(json)->value; } -int json_integer_set(json_t *json, long value) +int json_integer_set(json_t *json, json_int_t value) { if(!json_is_integer(json)) return -1; diff --git a/test/suites/invalid-strip/too-big-negative-integer/error b/test/suites/invalid-strip/too-big-negative-integer/error index 4245f39..1cfe2d5 100644 --- a/test/suites/invalid-strip/too-big-negative-integer/error +++ b/test/suites/invalid-strip/too-big-negative-integer/error @@ -1,2 +1,2 @@ 1 -too big negative integer near '-123123123123123' +too big negative integer diff --git a/test/suites/invalid-strip/too-big-negative-integer/input b/test/suites/invalid-strip/too-big-negative-integer/input index f2366dc..dfa3846 100644 --- a/test/suites/invalid-strip/too-big-negative-integer/input +++ b/test/suites/invalid-strip/too-big-negative-integer/input @@ -1 +1 @@ -[-123123123123123] \ No newline at end of file +[-123123123123123123123123123123] \ No newline at end of file diff --git a/test/suites/invalid-strip/too-big-positive-integer/error b/test/suites/invalid-strip/too-big-positive-integer/error index 4761ad3..89370e0 100644 --- a/test/suites/invalid-strip/too-big-positive-integer/error +++ b/test/suites/invalid-strip/too-big-positive-integer/error @@ -1,2 +1,2 @@ 1 -too big integer near '123123123123123' +too big integer diff --git a/test/suites/invalid-strip/too-big-positive-integer/input b/test/suites/invalid-strip/too-big-positive-integer/input index a787c2c..3156f10 100644 --- a/test/suites/invalid-strip/too-big-positive-integer/input +++ b/test/suites/invalid-strip/too-big-positive-integer/input @@ -1 +1 @@ -[123123123123123] \ No newline at end of file +[123123123123123123123123123123] \ No newline at end of file diff --git a/test/suites/invalid/too-big-negative-integer/error b/test/suites/invalid/too-big-negative-integer/error index 4245f39..1cfe2d5 100644 --- a/test/suites/invalid/too-big-negative-integer/error +++ b/test/suites/invalid/too-big-negative-integer/error @@ -1,2 +1,2 @@ 1 -too big negative integer near '-123123123123123' +too big negative integer diff --git a/test/suites/invalid/too-big-negative-integer/input b/test/suites/invalid/too-big-negative-integer/input index 40ed1bb..d6c26f1 100644 --- a/test/suites/invalid/too-big-negative-integer/input +++ b/test/suites/invalid/too-big-negative-integer/input @@ -1 +1 @@ -[-123123123123123] +[-123123123123123123123123123123] diff --git a/test/suites/invalid/too-big-positive-integer/error b/test/suites/invalid/too-big-positive-integer/error index 4761ad3..89370e0 100644 --- a/test/suites/invalid/too-big-positive-integer/error +++ b/test/suites/invalid/too-big-positive-integer/error @@ -1,2 +1,2 @@ 1 -too big integer near '123123123123123' +too big integer diff --git a/test/suites/invalid/too-big-positive-integer/input b/test/suites/invalid/too-big-positive-integer/input index d045071..27c8553 100644 --- a/test/suites/invalid/too-big-positive-integer/input +++ b/test/suites/invalid/too-big-positive-integer/input @@ -1 +1 @@ -[123123123123123] +[123123123123123123123123123123] -- 2.1.4