diff --git a/doc/apiref.rst b/doc/apiref.rst index 717e140..1230b10 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -97,29 +97,39 @@ functions: The type of a JSON value. The following members are defined: - +--------------------+ - | ``JSON_OBJECT`` | - +--------------------+ - | ``JSON_ARRAY`` | - +--------------------+ - | ``JSON_STRING`` | - +--------------------+ - | ``JSON_INTEGER`` | - +--------------------+ - | ``JSON_REAL`` | - +--------------------+ - | ``JSON_TRUE`` | - +--------------------+ - | ``JSON_FALSE`` | - +--------------------+ - | ``JSON_NULL`` | - +--------------------+ + +---------------------+ + | ``JSON_OBJECT`` | + +---------------------+ + | ``JSON_ARRAY`` | + +---------------------+ + | ``JSON_STRING`` | + +---------------------+ + | ``JSON_INTEGER`` | + +---------------------+ + | ``JSON_BIGINTEGER`` | + +---------------------+ + | ``JSON_REAL`` | + +---------------------+ + | ``JSON_BIGREAL`` | + +---------------------+ + | ``JSON_TRUE`` | + +---------------------+ + | ``JSON_FALSE`` | + +---------------------+ + | ``JSON_NULL`` | + +---------------------+ These correspond to JSON object, array, string, number, boolean and - null. A number is represented by either a value of the type - ``JSON_INTEGER`` or of the type ``JSON_REAL``. A true boolean value - is represented by a value of the type ``JSON_TRUE`` and false by a - value of the type ``JSON_FALSE``. + null. A number is represented by one of the values of type + ``JSON_INTEGER``, ``JSON_BIGINTEGER``, ``JSON_REAL``, or ``JSON_BIGREAL``. + A true boolean value is represented by a value of the type ``JSON_TRUE`` + and false by a value of the type ``JSON_FALSE``. + + The two big-number types, ``JSON_BIGINTEGER`` and ``JSON_BIGREAL``, + are used with add-on extensions to allow arbitrarily large or + high-precision numbers to be represented. Unless an extension is + provided and enabled using the big number extension API, these + types will not be used. .. function:: int json_typeof(const json_t *json) @@ -131,7 +141,9 @@ functions: json_is_array(const json_t *json) json_is_string(const json_t *json) json_is_integer(const json_t *json) + json_is_biginteger(const json_t *json) json_is_real(const json_t *json) + json_is_bigreal(const json_t *json) json_is_true(const json_t *json) json_is_false(const json_t *json) json_is_null(const json_t *json) @@ -143,7 +155,28 @@ functions: .. function:: json_is_number(const json_t *json) Returns true for values of types ``JSON_INTEGER`` and - ``JSON_REAL``, and false for other types and for *NULL*. + ``JSON_REAL``; and false for other types and for *NULL*. + +.. function:: json_is_bignumber(const json_t *json) + + Returns true for values of types ``JSON_BIGINTEGER`` and + ``JSON_BIGREAL``, and false for other types and for *NULL*. + +.. function:: json_is_anynumber(const json_t *json) + + Returns true for values of types ``JSON_INTEGER``, + ``JSON_BIGINTEGER``, ``JSON_REAL`` and ``JSON_BIGREAL``, and false + for other types and for *NULL*. + +.. function:: json_is_anyinteger(const json_t *json) + + Returns true for values of types ``JSON_INTEGER`` and + ``JSON_BIGINTEGER``, and false for other types and for *NULL*. + +.. function:: json_is_anyreal(const json_t *json) + + Returns true for values of types ``JSON_REAL`` and + ``JSON_BIGREAL``, and false for other types and for *NULL*. .. function:: json_is_boolean(const json_t *json) @@ -376,21 +409,55 @@ Number The JSON specification only contains one numeric type, "number". The C programming language has distinct types for integer and floating-point numbers, so for practical reasons Jansson also has distinct types for -the two. They are called "integer" and "real", respectively. For more -information, see :ref:`rfc-conformance`. +the two. They are called "integer" and "real", respectively. + +Additionally, Jansson provides an extension API to allow external +packages to be used to represent arbitrarily large integers or +arbitrarily high-precision real numbers. So a JSON number may be +represented by any of four different C types. + +For more information, see :ref:`rfc-conformance`. .. type:: 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``. + This is the C type that is used to store JSON integer values in the + absence of a big number extension. 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 the full 64-bit range, you should use ``json_int_t`` explicitly. +.. type:: double + + The C type ``double`` is used to store JSON real values in the + absence of a big number extension. + + Note that the C type ``long double`` is only supported by using the + big number extension API. + +.. type:: json_bigz_t +.. type:: json_bigz_const_t + + This is an unspecified C pointer type used to reference JSON + integer values that are stored using an external big number + package. The ``_const_t`` type is the same only it is used to + point to a constant value. + +.. type:: json_bigr_t +.. type:: json_bigr_const_t + + This is an unspecified C pointer type used to reference JSON real + values that are stored using an external big number package. The + ``_const_t`` type is the same only it is used to point to a + constant value. + +All of the functions and macros for dealing with the big number types +:type:`json_bigz_t` and :type:`json_bigr_t` are documented separately +in the section :ref:`apiref-big-number-extension`. + ``JSON_INTEGER_IS_LONG_LONG`` This is a preprocessor variable that holds the value 1 if :type:`json_int_t` is ``long long``, and 0 if it's ``long``. It @@ -407,7 +474,7 @@ information, see :ref:`rfc-conformance`. specifier that corresponds to :type:`json_int_t`, without the leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro is required because the actual type of :type:`json_int_t` can be - either ``long`` or ``long long``, and :func:`printf()` reuiqres + either ``long`` or ``long long``, and :func:`printf()` requires different length modifiers for the two. Example:: @@ -1009,6 +1076,40 @@ macros can be ORed together to obtain *flags*. .. versionadded:: 2.6 +``JSON_USE_BIGINT`` + This will enable the use of a big number package to be used to + store large integer values, assuming a suitable big number package + has been registered. Only those JSON integers whose values can not + be stored in a :type:`json_int_t` will use the big number + extension. + + .. versionadded:: 2.6 + +``JSON_USE_BIGINT_ALWAYS`` + This flag implies ``JSON_USE_BIGINT`` and differs by using the big + number extension to store all JSON integers, even those that could + have otherwise been stored in a :type:`json_int_t`. + + .. versionadded:: 2.6 + +``JSON_USE_BIGREAL`` + This will enable the use of a big number package to be used to + store large real values, assuming a suitable big number package has + been registered. Only those JSON reals whose values can not be + accurately stored in a :type:`json_real_t`, either because their + values or exponents are out of range or there would be a loss of + precision (dropped significant digits), will use the big number + extension. + + .. versionadded:: 2.6 + +``JSON_USE_BIGREAL_ALWAYS`` + This flag implies ``JSON_USE_BIGREAL`` and differs by using the big + number extension to store all JSON reals, even those that could + have otherwise been accurately stored in a :type:`json_real_t`. + + .. versionadded:: 2.6 + 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 @@ -1170,9 +1271,21 @@ arguments. ``I`` (integer) [json_int_t] Convert a C :type:`json_int_t` to JSON integer. +``z`` (big integer) [json_bigz_t] + Convert a C :type:`json_bigz_const_t` (a constant-pointer version + of a :type:`json_bigz_t`) to a JSON integer. The provided value + will be copied, no reference to it will be maintained. You must + have already registered a suitable big number package extension. + ``f`` (real) [double] Convert a C :type:`double` to JSON real. +``r`` (big real) [json_bigr_t] + Convert a C :type:`json_bigr_const_t` (a constant-pointer version + of a :type:`json_bigr_t`) to a JSON real. The provided value will + be copied, no reference to it will be maintained. You must have + already registered a suitable big number package extension. + ``o`` (any value) [json_t \*] Output any given JSON value as-is. If the value is added to an array or object, the reference to the value passed to ``o`` is @@ -1290,18 +1403,47 @@ type whose address should be passed. ``I`` (integer) [json_int_t] Convert a JSON integer to C :type:`json_int_t`. +``z`` (big integer) [json_bigz_t] + Convert a JSON big integer to a C :type:`json_bigz_t`. The + returned pointer will reference a newly allocated big number; you + are responsible for eventually freeing it. You must have already + registered a suitable big number package extension. + +``Z`` (any integer) [json_bigz_t] + Like ``z``, except that both plain integers and big integers will + be accepted. When extracting values, a big integer will always be + returned. + ``f`` (real) [double] Convert a JSON real to C :type:`double`. ``F`` (integer or real) [double] Convert a JSON number (integer or real) to C :type:`double`. +``r`` (big real) [json_bigr_t] + Convert a JSON big real to a C :type:`json_bigr_t`. The returned + pointer will reference newly allocated big number; you are + responsible for eventually freeing it. You must have already + registered a suitable big number package extension. + +``R`` (any integer) [json_bigr_t] + Like``r``, except that both plain reals and big reals will be + accepted. When extracting values, a big real will always be + returned. + ``o`` (any value) [json_t \*] Store a JSON value with no conversion to a :type:`json_t` pointer. ``O`` (any value) [json_t \*] Like ``O``, but the JSON value's reference count is incremented. +``v`` (any scalar) [json_t \*] + Store any JSON scalar value (any type except lists or objects) + with no conversion to a :type:`json_t` pointer. + +``V`` (any scalar) [json_t \*] + Like ``v``, but the JSON value's reference count is incremented. + ``[fmt]`` (array) Convert each item in the JSON array according to the inner format string. ``fmt`` may contain objects and arrays, i.e. recursive @@ -1356,6 +1498,17 @@ The following functions compose the parsing and validation API: behaviour of the unpacker, see below for the flags. Returns 0 on success and -1 on failure. +.. note:: + + The unpacking will halt at the first error, which may leave some of + the variables that you designated to hold unpacked values in an + uninitialized state. In partciular, as the big number formats + (``z``, ``Z``, ``r``, and ``R``) return pointers to newly allocated + memory, it is good practice to always initialize those + corresponding variables to *NULL* prior to unpacking, and to + remember to free the memory for those (if not null) afterwards, + regardless if the unpacking succeeded or resulted in an error. + .. note:: The first argument of all unpack functions is ``json_t *root`` @@ -1424,9 +1577,11 @@ exactly the same JSON value. However, two JSON values can be equal not only if they are exactly the same value, but also if they have equal "contents": -* Two integer or real values are equal if their contained numeric - values are equal. An integer value is never equal to a real value, - though. +* Two integer or two real values are equal if their contained numeric + values are equal. Comparisons between big number and corresponding + regular number types are allowable, and are likewise determined based + upon their equivalent numeric values. However, an integer value is + never equal to a real value. * Two strings are equal if their contained UTF-8 strings are equal, byte by byte. Unicode comparison algorithms are not implemented. @@ -1487,8 +1642,8 @@ Custom Memory Allocation ======================== By default, Jansson uses :func:`malloc()` and :func:`free()` for -memory allocation. These functions can be overridden if custom -behavior is needed. +memory allocation. These and other memory-related functions can be +overridden if custom behavior is needed. .. type:: json_malloc_t @@ -1504,6 +1659,23 @@ behavior is needed. typedef void (*json_free_t)(void *); +.. type:: json_realloc_t + + A typedef for a function pointer with :func:`realloc()`'s + signature:: + + typedef void (*json_realloc_t)(void *, size_t); + + Jansson does not itself use a realloc function, though it will pass + it on to big number extensions that may require a realloc. + +.. type:: json_overwrite_t + + A typedef for a function pointer that will securely overwrite + a region of memory that has a signature:: + + typedef void (*json_overwrite_t)(void *, size_t); + .. function:: void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) Use *malloc_fn* instead of :func:`malloc()` and *free_fn* instead @@ -1511,6 +1683,12 @@ behavior is needed. Jansson's API functions to ensure that all memory operations use the same functions. + Supplying *NULL* as either function pointer will restore the default + of using :func:`malloc()` or :func:`free()`. + + .. versionchanged:: 2.6 + Restore the default by supplying NULL. + **Examples:** Circumvent problems with different CRT heaps on Windows by using @@ -1522,11 +1700,19 @@ Use the `Boehm's conservative garbage collector`_ for memory operations:: json_set_alloc_funcs(GC_malloc, GC_free); + json_set_realloc_func(GC_realloc) .. _Boehm's conservative garbage collector: http://www.hpl.hp.com/personal/Hans_Boehm/gc/ Allow storing sensitive data (e.g. passwords or encryption keys) in -JSON structures by zeroing all memory when freed:: +JSON structures by zeroing all memory when freed. You do not need to +provide the secure_realloc function unless you use a big number +extension which uses realloc:: + + static void secure_overwrite(void *ptr, size_t size) + { + guaranteed_memset(ptr, 0, size); + } static void *secure_malloc(size_t size) { @@ -1543,13 +1729,40 @@ JSON structures by zeroing all memory when freed:: ptr -= 8; size = *((size_t *)ptr); - guaranteed_memset(ptr, 0, size + 8); + secure_overwrite(ptr, size + 8); free(ptr); } + static void *secure_realloc(void *ptr, size_t size) + { + size_t oldsize; + + if(ptr == NULL) + return secure_malloc(size); + if(size == 0) { + secure_free(ptr); + return NULL; + } + + oldsize = *((size_t *)(ptr - 8)); + + if( oldsize > size ) { + secure_overwrite(ptr+size, oldsize-size); + } + else if( oldsize < size ) { + void *newptr = secure_malloc(size); + memcpy(newptr, ptr, size); + secure_free(ptr); + ptr = newptr; + } + return ptr; + } + int main() { json_set_alloc_funcs(secure_malloc, secure_free); + json_set_realloc_func(secure_realloc); + json_set_overwrite_func(secure_overwrite); /* ... */ } @@ -1558,3 +1771,435 @@ memory, see http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html. The page also explains the :func:`guaranteed_memset()` function used in the example and gives a sample implementation for it. + +.. function:: void json_set_realloc_func(json_realloc_t realloc_fn) + + Uses *realloc_fn* instead of :func:`realloc()`. This function will + get passed to any big number extension for its possible use. Note + that Jansson does not itself use realloc. This function should be + called before any other of Jansson's APIs are called which may deal + with big number types. + + Supplying *NULL* as the function pointer will restore the default of + using :func:`realloc`. + +.. function:: void json_set_overwrite_func(json_overwrite_t overwrite_fn) + + Uses *overwrite_fn* to overwrite a region of memory. The default is + to use an internal function that wraps :func:`memset()` to write + zero-bytes. However as some systems may optimize away the memset + technique, a caller may wish to provide a more secure memory + overwriting function. + + Supplying *NULL* as the function pointer will restore the default + of using an internal :func:`memset()` based wrapper. + + +.. _apiref-big-number-extension: + +Big Number Extensions +===================== + +It is possible to extend Jansson so that it may support numeric values +that are larger or more precise than may be represented by the native +C types. Big number extensions may be independently provided for +integer values and real values. + +The extension API is designed to be generic so that many different big +number packages may be used. Each extension is enabled by registering +a set of user-supplied callback functions that perform basic +operations on a big number. These callbacks will often be thin +wrapper functions around the routines provided by the big number +package being used. + +For more information on big numbers and a list of some software +packages which support them see +http://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic + + +Big number types and functions +------------------------------ + +Within Jansson, a big number type is represented as an opaque pointer +type of ``json_bigz_t`` and ``json_bigr_t`` for big integers and big +reals respectively. There are corresponding constant-pointer types +as well, ``json_bigz_const_t`` and ``json_bigr_const_t``. + +By default these pointer types are declared as ``void *`` or ``void +const *``. To allow for better type safety, the user may provide a +more specific type name for these pointers by defining a macro prior +to including the Jansson header file; for example if using GMP:: + + #define JSON_BIGZ_TYPE mpz_t + #define JSON_BIGR_TYPE mpf_t + #include + +Then the ``json_bigz_t`` type will be equivalent to ``mpz_t *``, +and similar for the real types. + +.. note:: Jansson adopts the convention of using the letter *Z* to mean + integer and *R* to mean a real number. Do not let this cause you + confusion, as some big number packages may use *F* for reals + instead. + +.. function:: json_t *json_biginteger(json_bigz_const_t value); + + .. refcounting:: new + + Returns a new JSON big integer, or *NULL* on error. The passed-in + value is copied. + +.. function:: json_bigz_const_t json_biginteger_value(const json_t *biginteger) + + Returns the pointer to the value of *biginteger*, or *NULL* if it + is not a JSON big integer. Note that the returned pointer is a + reference to the existing value and not a copy; use caution if + retaining this reference. + +.. function:: int json_biginteger_set(json_t *integer, json_bigz_const_t value) + + Sets the associated value of *biginteger* to *value*. Returns 0 on + success and -1 if *biginteger* is not a JSON big integer. A copy is + made of *value*. + +.. function:: json_t *json_bigreal(json_bigr_const_t value); + + .. refcounting:: new + + Returns a new JSON big real, or *NULL* on error. The passed-in + value is copied. + +.. function:: json_bigr_const_t json_bigreal_value(const json_t *bigreal) + + Returns the pointer to the value of *bigreal*, or *NULL* if it is + not a JSON big real. Note that the returned pointer is a reference + to the existing value and not a copy; use caution if retaining this + reference. + +.. function:: int json_bigreal_set(json_t *bigreal, json_bigr_const_t value) + + Sets the associated value of *bigreal* to *value*. Returns 0 on + success and -1 if *bigreal* is not a JSON big real. A copy is + made of *value*. + +Sample bignum packages +---------------------- + +Included with the Jansson distribution are a sampling of extensions +for supporting several popular big number formats. These samples are C +code files which may be included and compiled into your own +application. + +*C long double:* The C standard provides a native type ``long double`` +which may have greater range and precision than a plain ``double`` +for real numbers. To use it add the sample file ``json_bignum_ldbl.c`` +into your project sources. Then arrange your own source similar to:: + + #define JSON_BIGR_TYPE long double + #include + + int main() + { + json_use_ldbl_for_bigreals(); + ... + } + +*GNU Quadmath:* The GNU libquadmath library provides support for +quad-precision floating-point numbers, which may have even greater +range and precision than a ``long double``. Quadmath is built into +newer versions of the GCC compiler on some platforms, so that it acts +like a native type. To use it add the sample file +``json_bignum_quad.c`` into your project sources. Then arrange your +own source similar to:: + + #include + #define JSON_BIGR_TYPE __float128 + #include + + int main() + { + json_use_quad_for_bigreals(); + ... + } + +Note that you may need to link your project with an additional system +library typically named *libquadmath* (use the ``-lquadmath`` linker option +in Unix-like environments). For more information see +http://gcc.gnu.org/onlinedocs/libquadmath/ + + +*GNU Multiprecision Library (GMP):* The GMP package supports arbitrary +sized integers and arbitrary precision real numbers. To use it add +the sample file ``json_bignum_gmp.c`` into your project sources. Then +arrange your own source similar to:: + + #include + #define JSON_BIGZ_TYPE mpz_t + #define JSON_BIGR_TYPE mpf_t + #include + + int main() + { + json_use_gmp_for_bigintegers(); + json_use_gmp_for_bigreals(); + mpf_set_default_prec( 1024 ); /* precision in bits */ + ... + } + +Notice that you may wish to call the GMP function +`mpf_set_default_prec` to set up the default number of bits +of precision that are kept. Jansson will always attempt to preserve +all of the digits, but this GMP default if not set appropriately may +still result in loss of significant digits. + +You will need to link your project with the GMP library (use the +``-lgmp`` linker option in Unix-like environments). For more +information see http://gmplib.org/ + +*OpenSSL Big Numbers (BN):* The OpenSSL cryptographic library provides +generic support for arbitrary sized integers. To use it add the +sample file ``json_bignum_openssl.c`` into your project sources. Then +arrange your own source similar to:: + + #include + #define JSON_BIGZ_TYPE BIGNUM + #include + + int main() + { + json_use_openssl_for_bigintegers(); + ... + } + +You will need to link your project with the OpenSSL *crypto* library +(use the ``-lcrypto`` linker option in Unix-like environments). For +more information see http://www.openssl.org/docs/crypto/bn.html + + +The callback functions +---------------------- + +To use a big number package you must provide a set of callback +functions that manipulate big number values. There are six callbacks +needed: copy, delete, compare, convert to string, convert from string, +and up-convert from a native number type. The callbacks are generally +the same for both big integers and big reals except for the specific +type signatures. + + +When invoked by Jansson, all of the callback function will be provided +with a ``json_memory_funcs_t`` argument that contains the set of +function pointers for common memory-related routines. The callback +functions should make use of these functions when possible so that +both Jansson and the big number package use compatible memory +management routines. The members of the ``json_memory_funcs_t`` +include:: + + malloc_fn(size_t size) + free_fn(void *ptr) + realloc_fn(void *ptr, size_t size) + overwrite_fn(void *ptr, size_t size) + strdup_fn(const char *str) + +Each big number package must provide the following callback functions, +shown here for big integers: + +.. type:: json_bigint_copy_t + + A typedef for a function pointer that will make a copy of a big integer value. It has a signature:: + + typedef json_bigz_t (*json_bigint_copy_t)(json_bigz_const_t bignum, const json_memory_funcs_t *memfuncs) + +.. type:: json_bigint_del_t + + A typedef for a function pointer that will delete a big integer value. It has a signature:: + + typedef void (*json_bigint_del_t)(json_bigz_t bignum, const json_memory_funcs_t *memfuncs) + +.. type:: json_bigint_cmp_t + + A typedef for a function pointer that will numerically compare two big integer values, returning -1 (less-than), 0 (equal), or +1 (greater-than). It has a signature:: + + typedef int (*json_bigint_cmp_t)(json_bigz_const_t bignum1, json_bigz_const_t bignum2, const json_memory_funcs_t *memfuncs) + +.. type:: json_bigint_to_str_t + + A typedef for a function pointer that will convert a big number + value into a decimal string format. The resulting format must + adhere strictly to the JSON standard syntax, e.g., "+0" or ".3" + are invalid. It has a signature:: + + typedef int (*json_bigint_to_str_t)(json_bigz_const_t bignum, char *buffer, size_t size, const json_memory_funcs_t *memfuncs) + + The function will be provided a buffer, identified with ``buffer`` + and ``size``, into which it should write the string. The string + must be null terminated. The length of the string excluding the + null byte must be returned. + + If the resulting string is too large to fit into the provided + buffer, then the function should return the number of bytes needed + (excluding the null terminator), after which the function will be + called again with a larger buffer. In this case it is not + necessary to write into the buffer or insure it is null + terminated. + +.. type:: json_bigint_from_str_t + + A typedef for a function pointer that will convert a decimal + string repreentation of a number (in standard JSON syntax) into a + big number value. It has the signature:: + + typedef json_bigz_t (*json_bigint_from_str_t)(const char *value, const json_memory_funcs_t *memfuncs) + + The returned value should be a newly-allocated big number type. If + the string value can not be converted then *NULL* should be + returned. + +.. type:: json_bigint_from_int_t + + A typedef for a function pointer that will convert a standard + native type of :type:`json_int_t` into a big number value. It has the signture:: + + typedef json_bigz_t (*json_bigint_from_int_t)(json_int_t value, const json_memory_funcs_t *memfuncs) + + The returned value should be a newly-allocated big number type. + +To register the callback functions, they must first be assembled into a structure of type :type:`json_bigint_funcs_t` or :type:`json_bigreal_funcs_t`, for example:: + + json_bigint_funcs_t callbacks; + callbacks.copy_fn = my_copy_function; + callbacks.delete_fn = my_delete_function; + callbacks.compare_fn = my_compare_function; + callbacks.to_string_fn = my_to_string_function; + callbacks.from_string_fn = my_from_string_function; + callbacks.from_int_fn = my_from_int_function; + +Callbacks for big reals are similar except the last member is named ``from_real_fn`` instead of ``from_int_fn``. + +Then the whole set of callbacks are registered with a single API call. + +.. function:: void json_set_biginteger_funcs(const json_bigint_funcs_t* functions) + + Registers the set of callback functions to use for manipulating + big integer values. Passing *NULL* will unregister any callback + functions and disable the use of big integers. This function + should be called before any other functions that may involve big + integers, such as loading JSON. + +.. function:: void json_set_bigreal_funcs(const json_bigreal_funcs_t* functions) + + Registers the set of callback functions to use for manipulating + big real values. Passing *NULL* will unregister any callback + functions and disable the use of big reals. This function should + be called before any other functions that may involve big + integers, such as loading JSON. + +Example using long double +------------------------- + +The following example code shows how to use the big number extensions +to support the native C type of ``long double`` for big real numbers. +This is essentially the same as what is in the supplied +``json_bignum_ldbl.c`` sample:: + + #include + #include + #include + #include + + #define JSON_BIGR_TYPE long double + #include + + /* These typedefs are now in effect: + json_bigr_t => long double * + json_bigr_const_t => long double const * + */ + + static int json_bigreal_ldbl_compare(json_bigr_const_t r1, json_bigr_const_t r2, + const json_memory_funcs_t *memfuncs) + { + const long double * f1 = r1; + const long double * f2 = r2; + + if( *f1 == *f2 ) return 0; + if( *f1 < *f2 ) return -1; + return 1; + } + + static json_bigr_t json_bigreal_ldbl_copy(json_bigr_const_t r, + const json_memory_funcs_t *memfuncs) + { + const long double * f1 = r; + long double * f2; + + f2 = memfuncs->malloc_fn( sizeof(long double) ); + if(!f2) + return NULL; + *f2 = *f1; + return f2; + } + + static void json_bigreal_ldbl_delete(json_bigr_t r, + const json_memory_funcs_t *memfuncs) + { + long double * f = r; + + memfuncs->free_fn( f ); + return; + } + + static json_bigr_t json_bigreal_ldbl_from_real(double value, + const json_memory_funcs_t *memfuncs) + { + long double * f; + + f = memfuncs->malloc_fn( sizeof(long double) ); + if(!f) + return NULL; + *f = value; + return f; + } + + static json_bigr_t json_bigreal_ldbl_from_str(const char *value, + const json_memory_funcs_t *memfuncs) + { + long double f0; + long double *f1; + + errno = 0; + f0 = strtold( value, NULL ); + + f1 = json_bigreal_ldbl_copy( &f0, memfuncs ); + memfuncs->overwrite_fn( &f0, sizeof(long double) ); + return f1; + } + + static int json_bigreal_ldbl_to_str(json_bigr_const_t r, char *buffer, size_t size, + const json_memory_funcs_t *memfuncs) + { + const long double *f = r; + int outsize; + + outsize = snprintf( buffer, size, "%.*Lg", LDBL_DIG, *f ); + return outsize; + } + + int json_use_ldbl_for_bigreals() + { + static json_bigreal_funcs_t funcs; + funcs.copy_fn = json_bigreal_ldbl_copy; + funcs.delete_fn = json_bigreal_ldbl_delete; + funcs.compare_fn = json_bigreal_ldbl_compare; + funcs.to_string_fn = json_bigreal_ldbl_to_str; + funcs.from_string_fn = json_bigreal_ldbl_from_str; + funcs.from_real_fn = json_bigreal_ldbl_from_real; + + json_set_bigreal_funcs( &funcs ); + return 0; + } + + int main() + { + json_use_ldbl_for_bigreals(); + ... + } diff --git a/doc/conformance.rst b/doc/conformance.rst index de3947d..92b767b 100644 --- a/doc/conformance.rst +++ b/doc/conformance.rst @@ -43,7 +43,9 @@ JSON makes no distinction between real and integer numbers; Jansson does. Real numbers are mapped to the ``double`` type and integers to 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. +compiler or not. Jansson also has an extension mechanism that allows +an externally provided big number package to be used to represent +arbitrary-sized integers and real numbers. A JSON number is considered to be a real number if its lexical representation includes one of ``e``, ``E``, or ``.``; regardless if @@ -61,6 +63,13 @@ represented in JSON as ``3.0``, not ``3``. Overflow, Underflow & Precision ------------------------------- +If a big number extension is used for either integer or real values, +then the behavior of all overflows, underflows, or loss of precision +is left to the extension. It is possible that no errors or loss of +information need ever occur. + +However lacking a big number extension there are limitations. + 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 platform, JSON numbers very close to zero such as 1E-999 may result in @@ -101,10 +110,12 @@ IEEE support the concept of signed integer zeros. Types ----- -No support is provided in Jansson for any C numeric types other than -``json_int_t`` and ``double``. This excludes things such as unsigned -types, ``long double``, etc. Obviously, shorter types like ``short``, +The only numeric types with direct built-in support in Jansson are the +``json_int_t`` and ``double`` types. 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. +to overflow semantics). + +Larger or more precise numeric types may be supported by using the big +number extension API; though such support depends upon an external big +number package. diff --git a/src/dump.c b/src/dump.c index 7eddd5a..77601ef 100644 --- a/src/dump.c +++ b/src/dump.c @@ -192,29 +192,102 @@ static int do_dump(const json_t *json, size_t flags, int depth, { char buffer[MAX_INTEGER_STR_LENGTH]; int size; + int rc; size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT, json_integer_value(json)); - if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) - return -1; - - return dump(buffer, size, data); + if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) { + rc = -1; + } + else { + rc = dump(buffer, size, data); + } + jsonp_overwrite(buffer, sizeof(buffer)); + return rc; } + case JSON_BIGINTEGER: + { + json_context_t *ctx; + json_bigz_const_t z; + int size; + int rc; + char buffer[MAX_INTEGER_STR_LENGTH * 5]; + + ctx = jsonp_context(); + if(!ctx->have_bigint) + return -1; + z = json_biginteger_value(json); + size = ctx->bigint.to_string_fn(z, buffer, sizeof(buffer), &ctx->memfuncs); + + if(size >= (int)sizeof(buffer)) { + /* Buffer was too small, allocate a bigger one */ + char* bigbuffer; + bigbuffer = ctx->memfuncs.malloc_fn(size + 4 /*extra for safety*/); + if(!bigbuffer) + return -1; + size = ctx->bigint.to_string_fn(z, bigbuffer, size, &ctx->memfuncs); + rc = dump(bigbuffer, size, data); + ctx->memfuncs.free_fn(bigbuffer); + } + else { + rc = dump(buffer, size, data); + } + ctx->memfuncs.overwrite_fn(buffer, sizeof(buffer)); + return rc; + } + case JSON_REAL: { char buffer[MAX_REAL_STR_LENGTH]; + int rc; int size; double value = json_real_value(json); size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value); - if(size < 0) - return -1; - - return dump(buffer, size, data); + if(size < 0) { + rc = -1; + } + else { + rc = dump(buffer, size, data); + } + jsonp_overwrite(buffer, sizeof(buffer)); + value = 0.0; + return rc; } + case JSON_BIGREAL: + { + json_context_t *ctx; + json_bigr_const_t r; + int size; + int rc; + char buffer[MAX_REAL_STR_LENGTH * 5]; + + ctx = jsonp_context(); + if(!ctx->have_bigreal) + return -1; + r = json_bigreal_value(json); + size = ctx->bigreal.to_string_fn(r, buffer, sizeof(buffer), &ctx->memfuncs); + + if(size >= (int)sizeof(buffer)) { + /* Buffer was too small, allocate a bigger one */ + char* bigbuffer; + bigbuffer = ctx->memfuncs.malloc_fn(size + 4 /*extra for safety*/); + if(!bigbuffer) + return -1; + size = ctx->bigreal.to_string_fn(r, bigbuffer, size, &ctx->memfuncs); + rc = dump(bigbuffer, size, data); + ctx->memfuncs.free_fn(bigbuffer); + } + else { + rc = dump(buffer, size, data); + } + ctx->memfuncs.overwrite_fn(buffer, sizeof(buffer)); + return rc; + } + case JSON_STRING: return dump_string(json_string_value(json), json_string_length(json), dump, data, flags); diff --git a/src/jansson.h b/src/jansson.h index c923a90..c2b567b 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -14,6 +14,10 @@ #include +#if USE_GNU_MP +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -36,6 +40,9 @@ extern "C" { /* types */ + +struct json_context; /* forward reference */ + typedef enum { JSON_OBJECT, JSON_ARRAY, @@ -44,7 +51,9 @@ typedef enum { JSON_REAL, JSON_TRUE, JSON_FALSE, - JSON_NULL + JSON_NULL, + JSON_BIGINTEGER, + JSON_BIGREAL } json_type; typedef struct json_t { @@ -66,13 +75,32 @@ typedef long json_int_t; #endif /* JSON_INTEGER_IS_LONG_LONG */ #endif +#ifndef JSON_BIGZ_TYPE +#define JSON_BIGZ_TYPE void +#endif +#ifndef JSON_BIGR_TYPE +#define JSON_BIGR_TYPE void +#endif + +typedef JSON_BIGZ_TYPE * json_bigz_t; +typedef JSON_BIGR_TYPE * json_bigr_t; +typedef JSON_BIGZ_TYPE const * json_bigz_const_t; +typedef JSON_BIGR_TYPE const * json_bigr_const_t; + + #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) #define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) #define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) +#define json_is_biginteger(json) (json && json_typeof(json) == JSON_BIGINTEGER) +#define json_is_anyinteger(json) (json_is_integer(json) || json_is_biginteger(json)) #define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) +#define json_is_bigreal(json) (json && json_typeof(json) == JSON_BIGREAL) +#define json_is_anyreal(json) (json_is_real(json) || json_is_bigreal(json)) #define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_bignumber(json) (json_is_biginteger(json) || json_is_bigreal(json)) +#define json_is_anynumber(json) (json_is_number(json) || json_is_bignumber(json)) #define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) #define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) #define json_boolean_value json_is_true @@ -93,6 +121,8 @@ json_t *json_true(void); json_t *json_false(void); #define json_boolean(val) ((val) ? json_true() : json_false()) json_t *json_null(void); +json_t *json_biginteger(json_bigz_const_t value); +json_t *json_bigreal(json_bigr_const_t value); static JSON_INLINE json_t *json_incref(json_t *json) @@ -214,6 +244,12 @@ int json_string_setn_nocheck(json_t *string, const char *value, size_t len); int json_integer_set(json_t *integer, json_int_t value); int json_real_set(json_t *real, double value); +json_bigz_const_t json_biginteger_value(const json_t *biginteger); +int json_biginteger_set(json_t *biginteger, json_bigz_const_t mpz); + +json_bigr_const_t json_bigreal_value(const json_t *bigreal); +int json_bigreal_set(json_t *bigreal, json_bigr_const_t mpf); + /* pack, unpack */ json_t *json_pack(const char *fmt, ...); @@ -258,13 +294,17 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla /* encoding */ -#define JSON_INDENT(n) (n & 0x1F) -#define JSON_COMPACT 0x20 -#define JSON_ENSURE_ASCII 0x40 -#define JSON_SORT_KEYS 0x80 -#define JSON_PRESERVE_ORDER 0x100 -#define JSON_ENCODE_ANY 0x200 -#define JSON_ESCAPE_SLASH 0x400 +#define JSON_INDENT(n) (n & 0x1F) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 +#define JSON_USE_BIGINT 0x800 +#define JSON_USE_BIGINT_ALWAYS 0x1000 +#define JSON_USE_BIGREAL 0x2000 +#define JSON_USE_BIGREAL_ALWAYS 0x4000 typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); @@ -273,12 +313,84 @@ int json_dumpf(const json_t *json, FILE *output, size_t flags); int json_dump_file(const json_t *json, const char *path, size_t flags); int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); -/* custom memory allocation */ +/* custom memory functions */ -typedef void *(*json_malloc_t)(size_t); -typedef void (*json_free_t)(void *); +typedef void *(*json_malloc_t)(size_t); /* allocate memory */ +typedef void (*json_free_t)(void *); /* free memory */ +typedef void *(*json_realloc_t)(void *, size_t); /* change allocation size */ +typedef void (*json_overwrite_t)(void *, size_t); /* overwrite memory */ +typedef char *(*json_strdup_t)(const char *); /* duplicate string */ + +typedef struct { + json_malloc_t malloc_fn; + json_free_t free_fn; + json_realloc_t realloc_fn; + json_overwrite_t overwrite_fn; + json_strdup_t strdup_fn; +} json_memory_funcs_t; void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); +void json_set_realloc_func(json_realloc_t realloc_fn); +void json_set_overwrite_func( json_overwrite_t overwrite_fn ); + + +/* big integers */ + +typedef json_bigz_t (*json_bigint_copy_t)(json_bigz_const_t bignum, + const json_memory_funcs_t *memfuncs); +typedef void (*json_bigint_del_t)(json_bigz_t bignum, + const json_memory_funcs_t *memfuncs); +typedef int (*json_bigint_cmp_t)(json_bigz_const_t bignum1, + json_bigz_const_t bignum2, + const json_memory_funcs_t *memfuncs); +typedef int (*json_bigint_to_str_t)(json_bigz_const_t bignum, + char *buffer, size_t size, + const json_memory_funcs_t *memfuncs); +typedef json_bigz_t (*json_bigint_from_str_t)(const char *value, + const json_memory_funcs_t *memfuncs); +typedef json_bigz_t (*json_bigint_from_int_t)(json_int_t value, + const json_memory_funcs_t *memfuncs); + +typedef struct { + json_bigint_copy_t copy_fn; + json_bigint_del_t delete_fn; + json_bigint_cmp_t compare_fn; + json_bigint_to_str_t to_string_fn; + json_bigint_from_str_t from_string_fn; + json_bigint_from_int_t from_int_fn; +} json_bigint_funcs_t; + +void json_set_biginteger_funcs(const json_bigint_funcs_t* functions); + + +/* Big reals */ + +typedef json_bigr_t (*json_bigreal_copy_t)(json_bigr_const_t bigreal, + const json_memory_funcs_t *memfuncs); +typedef void (*json_bigreal_del_t)(json_bigr_t bigreal, + const json_memory_funcs_t *memfuncs); +typedef int (*json_bigreal_cmp_t)(json_bigr_const_t bigreal1, + json_bigr_const_t bigreal2, + const json_memory_funcs_t *memfuncs); +typedef int (*json_bigreal_to_str_t)(json_bigr_const_t bigreal, + char *buffer, size_t size, + const json_memory_funcs_t *memfuncs); +typedef json_bigr_t (*json_bigreal_from_str_t)(const char *value, + const json_memory_funcs_t *memfuncs); +typedef json_bigr_t (*json_bigreal_from_real_t)(double value, + const json_memory_funcs_t *memfuncs); + +typedef struct { + json_bigreal_copy_t copy_fn; + json_bigreal_del_t delete_fn; + json_bigreal_cmp_t compare_fn; + json_bigreal_to_str_t to_string_fn; + json_bigreal_from_str_t from_string_fn; + json_bigreal_from_real_t from_real_fn; +} json_bigreal_funcs_t; + +void json_set_bigreal_funcs(const json_bigreal_funcs_t* functions); + #ifdef __cplusplus } diff --git a/src/jansson_private.h b/src/jansson_private.h index c6f437c..9d886f6 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -57,16 +57,28 @@ typedef struct { double value; } json_real_t; +typedef struct { + json_t json; + json_bigr_t value; +} json_bigreal_t; + typedef struct { json_t json; json_int_t value; } json_integer_t; +typedef struct { + json_t json; + json_bigz_t value; +} json_biginteger_t; + #define json_to_object(json_) container_of(json_, json_object_t, json) #define json_to_array(json_) container_of(json_, json_array_t, json) #define json_to_string(json_) container_of(json_, json_string_t, json) #define json_to_real(json_) container_of(json_, json_real_t, json) #define json_to_integer(json_) container_of(json_, json_integer_t, json) +#define json_to_biginteger(json_) container_of(json_, json_biginteger_t, json) +#define json_to_bigreal(json_) container_of(json_, json_bigreal_t, json) /* Create a string by taking ownership of an existing buffer */ json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); @@ -83,12 +95,27 @@ void jsonp_error_vset(json_error_t *error, int line, int column, int jsonp_strtod(strbuffer_t *strbuffer, double *out); int jsonp_dtostr(char *buffer, size_t size, double value); +/* For estimating precision needed to store a real number */ +int jsonp_count_significand_digits(strbuffer_t *strbuffer); + +/* Global context */ + +typedef struct json_context { + json_memory_funcs_t memfuncs; + int have_bigint; + int have_bigreal; + json_bigint_funcs_t bigint; + json_bigreal_funcs_t bigreal; +} json_context_t; + +json_context_t *jsonp_context(void); + /* Wrappers for custom memory functions */ -void* jsonp_malloc(size_t size); +void *jsonp_malloc(size_t size); void jsonp_free(void *ptr); -char *jsonp_strndup(const char *str, size_t length); char *jsonp_strdup(const char *str); char *jsonp_strndup(const char *str, size_t len); +void jsonp_overwrite(void *ptr, size_t size); /* Windows compatibility */ #ifdef _WIN32 diff --git a/src/load.c b/src/load.c index 56c8ee3..7805320 100644 --- a/src/load.c +++ b/src/load.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,12 @@ #define TOKEN_TRUE 259 #define TOKEN_FALSE 260 #define TOKEN_NULL 261 +#define TOKEN_BIGINTEGER 262 +#define TOKEN_BIGREAL 263 + +/* Big number functions */ +json_bigint_funcs_t* jsonp_biginteger_funcs = NULL; +json_bigreal_funcs_t* jsonp_bigreal_funcs = NULL; /* Locale independent versions of isxxx() functions */ #define l_isupper(c) ('A' <= (c) && (c) <= 'Z') @@ -69,11 +76,18 @@ typedef struct { } string; json_int_t integer; double real; + json_bigz_t bigz; + json_bigr_t bigr; } value; } lex_t; #define stream_to_lex(stream) container_of(stream, lex_t, stream) +/* forward references */ +static int lex_init(lex_t *, get_func, void *); +static void lex_clear(lex_t *); +static void lex_close(lex_t *); + /*** error reporting ***/ @@ -479,12 +493,14 @@ out: #endif #endif -static int lex_scan_number(lex_t *lex, int c, json_error_t *error) +static int lex_scan_number(lex_t *lex, int c, size_t flags, json_error_t *error) { + json_context_t *ctx; const char *saved_text; char *end; - double value; + int significand_digits; + ctx = jsonp_context(); lex->token = TOKEN_INVALID; if(c == '-') @@ -494,6 +510,8 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) c = lex_get_save(lex, error); if(l_isdigit(c)) { lex_unget_unsave(lex, c); + error_set(error, lex, + "numbers may not have unnecessary leading zeros"); goto out; } } @@ -508,26 +526,44 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) } if(c != '.' && c != 'E' && c != 'e') { - json_int_t value; - lex_unget_unsave(lex, c); saved_text = strbuffer_value(&lex->saved_text); 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); + if(flags & JSON_USE_BIGINT_ALWAYS) { + json_bigz_t bigvalue; + bigvalue = ctx->bigint.from_string_fn(saved_text, &ctx->memfuncs); + lex->token = TOKEN_BIGINTEGER; + lex->value.bigz = bigvalue; + } + else { + json_int_t value; + + value = json_strtoint(saved_text, &end, 10); + if(errno == ERANGE) { + if(flags & JSON_USE_BIGINT) { + json_bigz_t bigvalue; + bigvalue = ctx->bigint.from_string_fn(saved_text, &ctx->memfuncs); + lex->token = TOKEN_BIGINTEGER; + lex->value.bigz = bigvalue; + } + else { + if(value < 0) + error_set(error, lex, "too big negative integer"); + else + error_set(error, lex, "too big integer"); + goto out; + } + } + else { + assert(end == saved_text + lex->saved_text.length); + lex->token = TOKEN_INTEGER; + lex->value.integer = value; + } + } - lex->token = TOKEN_INTEGER; - lex->value.integer = value; return 0; } @@ -544,6 +580,13 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) c = lex_get_save(lex, error); } + if((flags & JSON_USE_BIGREAL) && ! (flags & JSON_USE_BIGREAL_ALWAYS)) { + /* Determine digits of precision needed to store number before + * a partial loss of precision occurs. + */ + significand_digits = jsonp_count_significand_digits(&lex->saved_text); + } + if(c == 'E' || c == 'e') { c = lex_get_save(lex, error); if(c == '+' || c == '-') @@ -561,27 +604,48 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) lex_unget_unsave(lex, c); - if(jsonp_strtod(&lex->saved_text, &value)) { - error_set(error, lex, "real number overflow"); - goto out; + if((flags & JSON_USE_BIGREAL_ALWAYS) || + ((flags & JSON_USE_BIGREAL) && significand_digits+1 >= DBL_DIG)) { + json_bigr_t bigvalue = NULL; + saved_text = strbuffer_value(&lex->saved_text); + bigvalue = ctx->bigreal.from_string_fn(saved_text, &ctx->memfuncs); + lex->token = TOKEN_BIGREAL; + lex->value.bigr = bigvalue; + } + else { + double value; + int rc; + + rc = jsonp_strtod(&lex->saved_text, &value); + if(errno == ERANGE && (flags & JSON_USE_BIGREAL)) { + /* overflow or underflow */ + json_bigr_t bigvalue; + saved_text = strbuffer_value(&lex->saved_text); + bigvalue = ctx->bigreal.from_string_fn(saved_text, &ctx->memfuncs); + lex->token = TOKEN_BIGREAL; + lex->value.bigr = bigvalue; + } + else if(rc != 0) { + error_set(error, lex, "real number overflow"); + goto out; + } + else { + lex->token = TOKEN_REAL; + lex->value.real = value; + } } - lex->token = TOKEN_REAL; - lex->value.real = value; return 0; out: return -1; } -static int lex_scan(lex_t *lex, json_error_t *error) +static int lex_scan(lex_t *lex, size_t flags, json_error_t *error) { int c; - strbuffer_clear(&lex->saved_text); - - if(lex->token == TOKEN_STRING) - lex_free_string(lex); + lex_clear(lex); c = lex_get(lex, error); while(c == ' ' || c == '\t' || c == '\n' || c == '\r') @@ -606,7 +670,7 @@ static int lex_scan(lex_t *lex, json_error_t *error) lex_scan_string(lex, error); else if(l_isdigit(c) || c == '-') { - if(lex_scan_number(lex, c, error)) + if(lex_scan_number(lex, c, flags, error)) goto out; } @@ -664,10 +728,29 @@ static int lex_init(lex_t *lex, get_func get, void *data) return 0; } +static void lex_clear(lex_t *lex) +{ + if(lex->token == TOKEN_STRING) { + lex_free_string(lex); + } + else if(lex->token == TOKEN_BIGINTEGER) { + json_context_t *ctx = jsonp_context(); + if(ctx->have_bigint) + ctx->bigint.delete_fn(lex->value.bigz, &ctx->memfuncs); + lex->value.bigz = NULL; + } + else if(lex->token == TOKEN_BIGREAL) { + json_context_t *ctx = jsonp_context(); + if(ctx->have_bigreal) + ctx->bigreal.delete_fn(lex->value.bigr, &ctx->memfuncs); + lex->value.bigr = NULL; + } + strbuffer_clear(&lex->saved_text); +} + static void lex_close(lex_t *lex) { - if(lex->token == TOKEN_STRING) - lex_free_string(lex); + lex_clear(lex); strbuffer_close(&lex->saved_text); } @@ -682,7 +765,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) if(!object) return NULL; - lex_scan(lex, error); + lex_scan(lex, flags, error); if(lex->token == '}') return object; @@ -713,14 +796,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) } } - lex_scan(lex, error); + lex_scan(lex, flags, error); if(lex->token != ':') { jsonp_free(key); error_set(error, lex, "':' expected"); goto error; } - lex_scan(lex, error); + lex_scan(lex, flags, error); value = parse_value(lex, flags, error); if(!value) { jsonp_free(key); @@ -736,11 +819,11 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) json_decref(value); jsonp_free(key); - lex_scan(lex, error); + lex_scan(lex, flags, error); if(lex->token != ',') break; - lex_scan(lex, error); + lex_scan(lex, flags, error); } if(lex->token != '}') { @@ -761,7 +844,7 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) if(!array) return NULL; - lex_scan(lex, error); + lex_scan(lex, flags, error); if(lex->token == ']') return array; @@ -776,11 +859,11 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) } json_decref(elem); - lex_scan(lex, error); + lex_scan(lex, flags, error); if(lex->token != ',') break; - lex_scan(lex, error); + lex_scan(lex, flags, error); } if(lex->token != ']') { @@ -833,6 +916,16 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) break; } + case TOKEN_BIGINTEGER: { + json = json_biginteger(lex->value.bigz); + break; + } + + case TOKEN_BIGREAL: { + json = json_bigreal(lex->value.bigr); + break; + } + case TOKEN_REAL: { json = json_real(lex->value.real); break; @@ -875,9 +968,26 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) { + json_context_t *ctx = jsonp_context(); json_t *result; - lex_scan(lex, error); + if(flags & JSON_USE_BIGINT_ALWAYS) + flags |= JSON_USE_BIGINT; + if(flags & JSON_USE_BIGREAL_ALWAYS) + flags |= JSON_USE_BIGREAL; + + if((flags & JSON_USE_BIGINT) && !ctx->have_bigint) { + error_set(error, lex, + "Programming error: Not prepared to decode big integers"); + return NULL; + } + if((flags & JSON_USE_BIGREAL) && !ctx->have_bigreal) { + error_set(error, lex, + "Programming error: Not prepared to decode big reals"); + return NULL; + } + + lex_scan(lex, flags, error); if(!(flags & JSON_DECODE_ANY)) { if(lex->token != '[' && lex->token != '{') { error_set(error, lex, "'[' or '{' expected"); @@ -890,7 +1000,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) return NULL; if(!(flags & JSON_DISABLE_EOF_CHECK)) { - lex_scan(lex, error); + lex_scan(lex, flags, error); if(lex->token != TOKEN_EOF) { error_set(error, lex, "end of file expected"); json_decref(result); diff --git a/src/memory.c b/src/memory.c index ca44d6b..943ef47 100644 --- a/src/memory.c +++ b/src/memory.c @@ -16,16 +16,49 @@ #undef malloc #undef free -/* memory function pointers */ -static json_malloc_t do_malloc = malloc; -static json_free_t do_free = free; +/* current context */ +static json_context_t* jsonp_current_context_ptr = NULL; +static json_context_t jsonp_current_context; + +static void jsonp_overwrite_memset(void *ptr, size_t size) +{ + if(size==0 || ptr==NULL) + return; + memset(ptr, 0, size); +} + +json_context_t *jsonp_context(void) +{ + if(jsonp_current_context_ptr) + return jsonp_current_context_ptr; + + memset(&jsonp_current_context, 0, sizeof(json_context_t)); + + jsonp_current_context.memfuncs.malloc_fn = malloc; + jsonp_current_context.memfuncs.free_fn = free; + jsonp_current_context.memfuncs.realloc_fn = realloc; + jsonp_current_context.memfuncs.overwrite_fn = jsonp_overwrite_memset; + jsonp_current_context.memfuncs.strdup_fn = jsonp_strdup; + + jsonp_current_context.have_bigint = 0; + jsonp_current_context.have_bigreal = 0; + + jsonp_current_context_ptr = &jsonp_current_context; + return jsonp_current_context_ptr; +} + +/* memory functions */ +void jsonp_overwrite(void *ptr, size_t size) +{ + jsonp_context()->memfuncs.overwrite_fn(ptr, size); +} void *jsonp_malloc(size_t size) { if(!size) return NULL; - return (*do_malloc)(size); + return (jsonp_context()->memfuncs.malloc_fn)(size); } void jsonp_free(void *ptr) @@ -33,7 +66,7 @@ void jsonp_free(void *ptr) if(!ptr) return; - (*do_free)(ptr); + (jsonp_context()->memfuncs.free_fn)(ptr); } char *jsonp_strdup(const char *str) @@ -56,6 +89,53 @@ char *jsonp_strndup(const char *str, size_t len) void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) { - do_malloc = malloc_fn; - do_free = free_fn; + if(malloc_fn == NULL) + malloc_fn = malloc; + if(free_fn == NULL) + free_fn = free; + jsonp_context()->memfuncs.malloc_fn = malloc_fn; + jsonp_context()->memfuncs.free_fn = free_fn; + if(malloc_fn == malloc) + jsonp_context()->memfuncs.realloc_fn = realloc; + else + jsonp_context()->memfuncs.realloc_fn = NULL; } + +void json_set_realloc_func(json_realloc_t realloc_fn) +{ + jsonp_context()->memfuncs.realloc_fn \ + = realloc_fn ? realloc_fn : realloc; +} + +void json_set_overwrite_func(json_overwrite_t overwrite_fn) +{ + jsonp_context()->memfuncs.overwrite_fn \ + = overwrite_fn ? overwrite_fn : jsonp_overwrite_memset; +} + +void json_set_biginteger_funcs(const json_bigint_funcs_t* functions) +{ + json_context_t *ctx = jsonp_context(); + if(!functions) { + ctx->have_bigint = 0; + memset(&ctx->bigint, 0, sizeof(json_bigint_funcs_t)); + } + else { + ctx->have_bigint = 1; + memcpy(&ctx->bigint, functions, sizeof(json_bigint_funcs_t)); + } +} + +void json_set_bigreal_funcs(const json_bigreal_funcs_t* functions) +{ + json_context_t *ctx = jsonp_context(); + if(!functions) { + ctx->have_bigreal = 0; + memset(&ctx->bigreal, 0, sizeof(json_bigreal_funcs_t)); + } + else { + ctx->have_bigreal = 1; + memcpy(&ctx->bigreal, functions, sizeof(json_bigreal_funcs_t)); + } +} + diff --git a/src/pack_unpack.c b/src/pack_unpack.c index 76d946b..d8e1a48 100644 --- a/src/pack_unpack.c +++ b/src/pack_unpack.c @@ -41,12 +41,14 @@ static const char * const type_names[] = { "real", "true", "false", - "null" + "null", + "biginteger", + "bigreal" }; #define type_name(x) type_names[json_typeof(x)] -static const char unpack_value_starters[] = "{[siIbfFOon"; +static const char unpack_value_starters[] = "{[siIbfFOonzr"; static void scanner_init(scanner_t *s, json_error_t *error, @@ -328,9 +330,15 @@ static json_t *pack(scanner_t *s, va_list *ap) case 'I': /* integer from json_int_t */ return json_integer(va_arg(*ap, json_int_t)); + case 'z': /* big integer */ + return json_biginteger(va_arg(*ap,json_bigz_const_t)); + case 'f': /* real */ return json_real(va_arg(*ap, double)); + case 'r': /* big real */ + return json_bigreal(va_arg(*ap,json_bigr_const_t)); + case 'O': /* a json_t object; increments refcount */ return json_incref(va_arg(*ap, json_t *)); @@ -517,6 +525,8 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) static int unpack(scanner_t *s, json_t *root, va_list *ap) { + int do_incref = 0; + switch(token(s)) { case '{': @@ -592,6 +602,50 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) return 0; + case 'z': + if(!json_is_biginteger(root)) { + set_error(s,"", "Expected big integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_bigz_t v; + json_context_t *ctx = jsonp_context(); + if(!ctx->have_bigint) { + set_error(s,"", "No big integer package registed, can not unpack value"); + return -1; + } + v = ctx->bigint.copy_fn(json_biginteger_value(root), + &ctx->memfuncs); + *va_arg(*ap, json_bigz_t*) = v; + } + return 0; + + case 'Z': + if(!json_is_anyinteger(root)) { + set_error(s,"", "Expected an integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_bigz_t v; + json_context_t *ctx = jsonp_context(); + if(!ctx->have_bigint) { + set_error(s,"", "No big integer package registed, can not unpack value"); + return -1; + } + if(json_is_biginteger(root)) + v = ctx->bigint.copy_fn(json_biginteger_value(root), + &ctx->memfuncs); + else + v = ctx->bigint.from_int_fn(json_integer_value(root), + &ctx->memfuncs); + *va_arg(*ap, json_bigz_t*) = v; + } + return 0; + case 'b': if(root && !json_is_boolean(root)) { set_error(s, "", "Expected true or false, got %s", @@ -623,7 +677,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) return 0; case 'F': - if(root && !json_is_number(root)) { + if((root && !json_is_number(root)) || json_is_bignumber(root)) { set_error(s, "", "Expected real or integer, got %s", type_name(root)); return -1; @@ -637,18 +691,75 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) return 0; + case 'r': + if(!json_is_bigreal(root)) { + set_error(s,"", "Expected big real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_bigr_t v; + json_context_t *ctx = jsonp_context(); + if(!ctx->have_bigreal) { + set_error(s,"", "No big real package registed, can not unpack value"); + return -1; + } + v = ctx->bigreal.copy_fn(json_bigreal_value(root), + &ctx->memfuncs); + *va_arg(*ap, json_bigr_t*) = v; + } + return 0; + + case 'R': + if(!json_is_anyreal(root)) { + set_error(s,"", "Expected a real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_bigr_t v; + json_context_t *ctx = jsonp_context(); + if(!ctx->have_bigreal) { + set_error(s,"", "No big real package registed, can not unpack value"); + return -1; + } + if(json_is_bigreal(root)) + v = ctx->bigreal.copy_fn(json_bigreal_value(root), + &ctx->memfuncs); + else + v = ctx->bigreal.from_real_fn(json_real_value(root), + &ctx->memfuncs); + *va_arg(*ap, json_bigz_t*) = v; + } + return 0; + + case 'V': case 'O': if(root && !(s->flags & JSON_VALIDATE_ONLY)) - json_incref(root); + do_incref = 1; /* Fall through */ + case 'v': case 'o': + if(token(s) == 'V' || token(s) == 'v') { + if(json_is_array(root) || json_is_object(root)) { + set_error(s,"", "Expecting a scalar value, got %s", + type_name(root)); + return -1; + } + } if(!(s->flags & JSON_VALIDATE_ONLY)) { - json_t **target = va_arg(*ap, json_t**); + json_t **target; + + if(do_incref) + json_incref(root); + + target = va_arg(*ap, json_t**); if(root) *target = root; } - return 0; case 'n': diff --git a/src/strconv.c b/src/strconv.c index 3a70c6f..6609161 100644 --- a/src/strconv.c +++ b/src/strconv.c @@ -132,3 +132,33 @@ int jsonp_dtostr(char *buffer, size_t size, double value) return (int)length; } + +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isdigit19(c) ('1' <= (c) && (c) <= '9') + +int jsonp_count_significand_digits(strbuffer_t *strbuffer) +{ + int digits = 0; + const char* c; + /* Skip leading zeros and signs */ + for( c=strbuffer->value; *c=='0' || *c=='-' || *c=='.'; c++ ); + + /* Count digits */ + while( l_isdigit(*c) || *c == '.' ) { + if( l_isdigit19(*c) ) + digits++; + else if( *c=='0' ) { + const char* d; + int zerorun=0; + for( d=c; *d=='0' || *d=='.'; d++ ) + if( *d=='0' ) + zerorun++; + if( l_isdigit19(*d) ) { + digits += zerorun + 1; + c = d; + } + } + c++; + } + return digits; +} diff --git a/src/value.c b/src/value.c index 6d131d8..f72f7ab 100644 --- a/src/value.c +++ b/src/value.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "jansson.h" #include "hashtable.h" @@ -566,7 +567,7 @@ int json_array_extend(json_t *json, json_t *other_json) return 0; } -static int json_array_equal(json_t *array1, json_t *array2) +static int json_array_equal(const json_t *array1, const json_t *array2) { size_t i, size; @@ -750,7 +751,7 @@ static void json_delete_string(json_string_t *string) jsonp_free(string); } -static int json_string_equal(json_t *string1, json_t *string2) +static int json_string_equal(const json_t *string1, const json_t *string2) { json_string_t *s1, *s2; @@ -810,7 +811,7 @@ static void json_delete_integer(json_integer_t *integer) jsonp_free(integer); } -static int json_integer_equal(json_t *integer1, json_t *integer2) +static int json_integer_equal(const json_t *integer1, const json_t *integer2) { return json_integer_value(integer1) == json_integer_value(integer2); } @@ -821,6 +822,110 @@ static json_t *json_integer_copy(const json_t *integer) } +/*** big integer ***/ + +json_t *json_biginteger(json_bigz_const_t value) +{ + json_biginteger_t *bigint; + json_context_t* ctx = jsonp_context(); + + if(!ctx->have_bigint) + return NULL; + + bigint = jsonp_malloc(sizeof(json_biginteger_t)); + if(!bigint) + return NULL; + json_init(&bigint->json, JSON_BIGINTEGER); + + bigint->value = ctx->bigint.copy_fn(value, &ctx->memfuncs); + return &bigint->json; +} + +json_bigz_const_t json_biginteger_value(const json_t *json) +{ + if(!json_is_biginteger(json)) + return NULL; + + return json_to_biginteger(json)->value; +} + +int json_biginteger_set(json_t* json, json_bigz_const_t value) +{ + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigint) + return -1; + + if(!json_is_biginteger(json)) + return -1; + + json_to_biginteger(json)->value = ctx->bigint.copy_fn(value, &ctx->memfuncs); + + return 0; +} + +static void json_delete_biginteger(json_biginteger_t *bigint) +{ + json_context_t *ctx = jsonp_context(); + + if(ctx->have_bigint) + ctx->bigint.delete_fn(bigint->value, &ctx->memfuncs); + jsonp_free(bigint); +} + +static int json_biginteger_equal(const json_t* bigint1, const json_t* bigint2) +{ + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigint) + return bigint1 == bigint2; + return ctx->bigint.compare_fn(json_biginteger_value(bigint1), + json_biginteger_value(bigint2), + &ctx->memfuncs) == 0; +} + +static json_t* json_biginteger_copy(const json_t *bigint) +{ + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigint) + return NULL; + return ctx->bigint.copy_fn(json_biginteger_value(bigint), + &ctx->memfuncs); +} + +static int json_anyinteger_equal(const json_t* int1, const json_t* int2) +{ + if(json_is_integer(int1) && json_is_integer(int2)) + return json_integer_equal(int1, int2); + else if(json_is_biginteger(int1) && json_is_biginteger(int2)) + return json_biginteger_equal(int1, int2); + + { + json_context_t *ctx = jsonp_context(); + json_bigz_const_t i1; + json_bigz_t i2; + int eq; + + if(!ctx->have_bigint) + return int1 == int2; + + if(json_is_biginteger(int1)) { + assert(json_is_integer(int2)); + i1 = json_biginteger_value(int1); + i2 = ctx->bigint.from_int_fn(json_integer_value(int2),&ctx->memfuncs); + } + else { + assert(json_is_integer(int1)); + i1 = json_biginteger_value(int2); + i2 = ctx->bigint.from_int_fn(json_integer_value(int1),&ctx->memfuncs); + } + eq = ctx->bigint.compare_fn(i1, i2, &ctx->memfuncs) == 0; + ctx->bigint.delete_fn(i2, &ctx->memfuncs); + return eq; + } +} + /*** real ***/ json_t *json_real(double value) @@ -862,7 +967,7 @@ static void json_delete_real(json_real_t *real) jsonp_free(real); } -static int json_real_equal(json_t *real1, json_t *real2) +static int json_real_equal(const json_t *real1, const json_t *real2) { return json_real_value(real1) == json_real_value(real2); } @@ -873,6 +978,110 @@ static json_t *json_real_copy(const json_t *real) } +/*** big real ***/ + +json_t *json_bigreal(json_bigr_const_t value) +{ + json_bigreal_t *bigreal; + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigreal) + return NULL; + + bigreal = jsonp_malloc(sizeof(json_bigreal_t)); + if(!bigreal) + return NULL; + json_init(&bigreal->json, JSON_BIGREAL); + + bigreal->value = ctx->bigreal.copy_fn(value, &ctx->memfuncs); + return &bigreal->json; +} + +json_bigr_const_t json_bigreal_value(const json_t *json) +{ + if(!json_is_bigreal(json)) + return NULL; + + return json_to_bigreal(json)->value; +} + +int json_bigreal_set(json_t* json, json_bigr_const_t value) +{ + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigreal) + return -1; + + if(!json_is_bigreal(json)) + return -1; + + json_to_bigreal(json)->value = ctx->bigreal.copy_fn(value, &ctx->memfuncs); + + return 0; +} + +static void json_delete_bigreal(json_bigreal_t *bigreal) +{ + json_context_t *ctx = jsonp_context(); + + if(ctx->have_bigreal) + ctx->bigreal.delete_fn(bigreal->value, &ctx->memfuncs); + jsonp_free(bigreal); +} + +static int json_bigreal_equal(const json_t* bigreal1, const json_t* bigreal2) +{ + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigreal) + return bigreal1 == bigreal2; + return ctx->bigreal.compare_fn(json_bigreal_value(bigreal1), + json_bigreal_value(bigreal2), + &ctx->memfuncs) == 0; +} + +static json_t* json_bigreal_copy(const json_t *bigreal) +{ + json_context_t *ctx = jsonp_context(); + + if(!ctx->have_bigreal) + return NULL; + return ctx->bigreal.copy_fn(json_bigreal_value(bigreal), + &ctx->memfuncs); +} + +static int json_anyreal_equal(const json_t* real1, const json_t* real2) +{ + if(json_is_real(real1) && json_is_real(real2)) + return json_real_equal(real1, real2); + else if(json_is_bigreal(real1) && json_is_bigreal(real2)) + return json_bigreal_equal(real1, real2); + + { + json_context_t *ctx = jsonp_context(); + json_bigr_const_t r1; + json_bigr_t r2; + int eq; + + if(!ctx->have_bigreal) + return real1 == real2; + + if(json_is_bigreal(real1)) { + assert(json_is_real(real2)); + r1 = json_bigreal_value(real1); + r2 = ctx->bigreal.from_real_fn(json_real_value(real2),&ctx->memfuncs); + } + else { + assert(json_is_real(real1)); + r1 = json_bigreal_value(real2); + r2 = ctx->bigreal.from_real_fn(json_real_value(real1),&ctx->memfuncs); + } + eq = ctx->bigreal.compare_fn(r1, r2, &ctx->memfuncs) == 0; + ctx->bigreal.delete_fn(r2, &ctx->memfuncs); + return eq; + } +} + /*** number ***/ double json_number_value(const json_t *json) @@ -928,6 +1137,12 @@ void json_delete(json_t *json) else if(json_is_real(json)) json_delete_real(json_to_real(json)); + else if(json_is_biginteger(json)) + json_delete_biginteger(json_to_biginteger(json)); + + else if(json_is_bigreal(json)) + json_delete_bigreal(json_to_bigreal(json)); + /* json_delete is not called for true, false or null */ } @@ -939,8 +1154,17 @@ int json_equal(json_t *json1, json_t *json2) if(!json1 || !json2) return 0; - if(json_typeof(json1) != json_typeof(json2)) - return 0; + if(json_typeof(json1) != json_typeof(json2)) { + /* Types not equal, see if they are convertable */ + if(json_is_anyinteger(json1) && json_is_anyinteger(json2)) { + return json_anyinteger_equal(json1, json2); + } + else if(json_is_anyreal(json1) && json_is_anyreal(json2)) { + return json_anyreal_equal(json1, json2); + } + else + return 0; + } /* this covers true, false and null as they are singletons */ if(json1 == json2) @@ -961,6 +1185,12 @@ int json_equal(json_t *json1, json_t *json2) if(json_is_real(json1)) return json_real_equal(json1, json2); + if(json_is_biginteger(json1)) + return json_biginteger_equal(json1, json2); + + if(json_is_bigreal(json2)) + return json_bigreal_equal(json1, json2); + return 0; } @@ -987,6 +1217,12 @@ json_t *json_copy(json_t *json) if(json_is_real(json)) return json_real_copy(json); + if(json_is_biginteger(json)) + return json_biginteger_copy(json); + + if(json_is_bigreal(json)) + return json_bigreal_copy(json); + if(json_is_true(json) || json_is_false(json) || json_is_null(json)) return json; @@ -1016,6 +1252,12 @@ json_t *json_deep_copy(const json_t *json) if(json_is_real(json)) return json_real_copy(json); + if(json_is_biginteger(json)) + return json_biginteger_copy(json); + + if(json_is_bigreal(json)) + return json_bigreal_copy(json); + if(json_is_true(json) || json_is_false(json) || json_is_null(json)) return (json_t *)json; diff --git a/test/suites/invalid/integer-starting-with-zero/error b/test/suites/invalid/integer-starting-with-zero/error index 64e0536..36074b3 100644 --- a/test/suites/invalid/integer-starting-with-zero/error +++ b/test/suites/invalid/integer-starting-with-zero/error @@ -1,2 +1,2 @@ 1 2 2 -invalid token near '0' +numbers may not have unnecessary leading zeros near '0' diff --git a/test/suites/invalid/negative-integer-starting-with-zero/error b/test/suites/invalid/negative-integer-starting-with-zero/error index 36adc34..a81e764 100644 --- a/test/suites/invalid/negative-integer-starting-with-zero/error +++ b/test/suites/invalid/negative-integer-starting-with-zero/error @@ -1,2 +1,2 @@ 1 3 3 -invalid token near '-0' +numbers may not have unnecessary leading zeros near '-0'