aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetri Lehtinen <petri@digip.org>2014-01-28 10:14:38 +0200
committerPetri Lehtinen <petri@digip.org>2014-01-28 16:07:04 +0200
commitf8716ac33629d02185ba61353da1dd09a564ae5f (patch)
treef11365231d3eab550dca51117742c23be882165c
parent30fdf6067e7d09e906fb76d45ce79269c02e7232 (diff)
downloadjansson-bignum.zip
jansson-bignum.tar.gz
jansson-bignum.tar.bz2
Rebase Deron Meranda's original bignum patchbignum
-rw-r--r--doc/apiref.rst717
-rw-r--r--doc/conformance.rst23
-rw-r--r--src/dump.c89
-rw-r--r--src/jansson.h134
-rw-r--r--src/jansson_private.h31
-rw-r--r--src/load.c186
-rw-r--r--src/memory.c94
-rw-r--r--src/pack_unpack.c123
-rw-r--r--src/strconv.c30
-rw-r--r--src/value.c254
-rw-r--r--test/suites/invalid/integer-starting-with-zero/error2
-rw-r--r--test/suites/invalid/negative-integer-starting-with-zero/error2
12 files changed, 1563 insertions, 122 deletions
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
@@ -1358,6 +1500,17 @@ The following functions compose the parsing and validation API:
.. 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``
instead of ``const json_t *root``, because the use of ``O`` format
specifier causes the reference count of ``root``, or some value
@@ -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 <jansson.h>
+
+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 <jansson.h>
+
+ 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 <quadmath.h>
+ #define JSON_BIGR_TYPE __float128
+ #include <jansson.h>
+
+ 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 <gmp.h>
+ #define JSON_BIGZ_TYPE mpz_t
+ #define JSON_BIGR_TYPE mpf_t
+ #include <jansson.h>
+
+ 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 <openssl/bn.h>
+ #define JSON_BIGZ_TYPE BIGNUM
+ #include <jansson.h>
+
+ 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 <string.h>
+ #include <stdlib.h>
+ #include <float.h>
+ #include <errno.h>
+
+ #define JSON_BIGR_TYPE long double
+ #include <jansson.h>
+
+ /* 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 <jansson_config.h>
+#if USE_GNU_MP
+#include <gmp.h>
+#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); /* 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 void *(*json_malloc_t)(size_t);
-typedef void (*json_free_t)(void *);
+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
@@ -59,14 +59,26 @@ typedef struct {
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 <errno.h>
#include <limits.h>
+#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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_close(lex_t *lex)
+static void lex_clear(lex_t *lex)
{
- if(lex->token == TOKEN_STRING)
+ 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)
+{
+ 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,"<validation>", "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,"<validation>", "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,"<validation>", "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,"<validation>", "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, "<validation>", "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, "<validation>", "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,"<validation>", "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,"<validation>", "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,"<validation>", "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,"<validation>", "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,"<validation>", "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 <stdlib.h>
#include <string.h>
#include <math.h>
+#include <assert.h>
#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'