Rebase Deron Meranda's original bignum patch

This commit is contained in:
Petri Lehtinen 2014-01-28 10:14:38 +02:00
parent 30fdf6067e
commit f8716ac336
12 changed files with 1563 additions and 122 deletions

View File

@ -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 <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();
...
}

View File

@ -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.

View File

@ -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);

View File

@ -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);
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
}

View File

@ -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

View File

@ -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_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);

View File

@ -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));
}
}

View File

@ -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':

View File

@ -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;
}

View File

@ -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;

View File

@ -1,2 +1,2 @@
1 2 2
invalid token near '0'
numbers may not have unnecessary leading zeros near '0'

View File

@ -1,2 +1,2 @@
1 3 3
invalid token near '-0'
numbers may not have unnecessary leading zeros near '-0'