44 Commits
v2.10 ... v2.11

Author SHA1 Message Date
Petri Lehtinen
6dddf687d8 Merge pull request #396 from coreyfarrell/2.11
jansson 2.11
2018-02-11 20:11:41 +02:00
Corey Farrell
744fe5ed44 jansson 2.11 2018-02-09 12:31:18 -05:00
Petri Lehtinen
03620980cf Merge pull request #395 from akheron/doc-utf-8
Document encoding requirements for callbacks
2018-02-09 10:10:34 +02:00
Petri Lehtinen
248d62111c Merge pull request #368 from AmeyaVS/cmake_build_fix
Fix generated `pkg-config` file using cmake.
2018-02-09 09:45:36 +02:00
Petri Lehtinen
46dff2737d Merge pull request #381 from phst/end-of-file-error-code
Use a more specific error code for premature end of input
2018-02-09 09:40:42 +02:00
Petri Lehtinen
fa0b5ece9e Merge pull request #380 from phst/doc-error-code
document that json_error_code will be added in version 2.11
2018-02-09 09:39:38 +02:00
Petri Lehtinen
a6138a07b6 Document encoding requirements for callbacks
Original patch by @phst.

Fixes #369.
2018-02-09 09:35:53 +02:00
Petri Lehtinen
2863dde053 Merge pull request #393 from akheron/json_sprintf
Add json_sprintf and json_vsprintf
2018-02-09 08:15:16 +02:00
Petri Lehtinen
efe6c7b3f2 Add json_sprintf and json_vsprintf
Fixes #392
2018-02-09 07:37:33 +02:00
Petri Lehtinen
3e81f78366 Merge pull request #389 from coreyfarrell/threadsafety
Atomic references and thread safe json_dump
2018-02-08 20:19:07 +02:00
Corey Farrell
8104ce167a Merge pull request #1 from akheron/threadsafety
Clarify thread safety docs, rename JANSSON_THREAD_SAFE
2018-02-08 11:58:30 -05:00
Petri Lehtinen
f44921e176 Clarify thread safety docs, rename JANSSON_THREAD_SAFE 2018-02-08 12:38:14 +02:00
Corey Farrell
3aee856d7b Docs: Update information on thread safety.
Fixes #387
2018-02-01 15:54:35 -05:00
Corey Farrell
37e0ee4d48 json_dump: Fix thread safety issue.
Circular reference detection in json_dump was not thread safe.  Replace
visited flag with a hashtable_t.

Issue #387
2018-02-01 15:54:25 -05:00
Corey Farrell
dc3b313e91 Use thread-safe reference counting if supported by the compiler.
This makes use of __atomic or __sync builtin compiler functions to make
json_decref and json_incref thread-safe.

Issue #387
2018-01-29 14:17:58 -05:00
Philipp Stephani
45228cada4 Use a more specific error code for premature end of input 2017-12-20 18:27:04 +01:00
Philipp Stephani
24d45272a7 document that json_error_code will be added in version 2.11 2017-12-18 23:51:32 +01:00
Petri Lehtinen
9e5af7c3b7 Merge pull request #374 from coreyfarrell/always-steal
json_pack: Enable more complete stealing of references.
2017-12-16 20:35:14 +02:00
Corey Farrell
6c78910011 apiref: Clarify documentation for unpack O format.
The `O` format causes reference counts to increase, but in an error they
are not released.  Callers to unpack functions that use the `O` format
should use pointers pre-initialized to NULL so they can safely release
the reference on error.

Also corrected typo which said this was like `O` (itself).

Fixes #135
2017-12-13 14:04:07 -05:00
Corey Farrell
89dad8959b json_object_iter_set_new: Fix error branch leak.
This function needs to release a reference to value if the other
arguments are invalid.

Issue #135
2017-12-13 14:04:07 -05:00
Corey Farrell
9a1d9c88fc json_pack: Enable more complete stealing of references.
Users of the "o" format have an expectation that the object reference
will be stolen.  Any error causes the collection process to end early.
This patch causes json_pack and related functions to continue scanning
the format and parameters so all references can be stolen to prevent
leaks.  This makes no attempt to continue processing if the format
string is broken or missing.

'make check' still passes.  Ran test_pack under valgrind and verified
that the leaked reference is fixed. Added a test which uses refcounts
to verify that the reference was correctly stolen after a NULL value
error.

Issue #135
2017-12-13 14:03:58 -05:00
Petri Lehtinen
02dade46c0 Merge pull request #375 from phniix/373_upstream_typo_in_cmakelists
Fixes akheron/jansson#373
2017-12-13 07:08:25 +02:00
Petri Lehtinen
bc5c6826ef Merge pull request #377 from coreyfarrell/extra-refs
Remove extra reference actions in parsers.
2017-12-13 07:07:33 +02:00
Petri Lehtinen
217859f849 Merge pull request #378 from phst/doc
Document that length-aware string functions have been added in 2.7
2017-12-13 07:05:15 +02:00
Philipp Stephani
3951d39b40 Document that length-aware string functions have been added in 2.7 2017-12-10 16:53:01 +01:00
Corey Farrell
bd91753e91 Remove extra reference actions in parsers.
Make parse_object use json_object_set_new_nocheck and make parse_array
use json_array_append_new, remove json_decref from error and success
paths.

Fixes #376
2017-11-14 23:52:49 -05:00
Joe Hura
0b04762c94 Fixes akheron/jansson#373
'sys/time.h' is a typo, it should read: 'sys/types.h'
2017-11-07 19:32:14 +11:00
Petri Lehtinen
009ffa3fc8 Fix a compile error on macOS clang
Reported by Diederick Huijbers
2017-11-07 10:20:21 +02:00
Ameya Vikram Singh
89f0dde7ff Fix generated pkg-config file using cmake.
Fixed the generated `jansson.pc` with cmake to be consistent with the
one generated using GNU Autotools.
2017-10-29 23:57:25 +05:30
Petri Lehtinen
9e7847ed26 Merge pull request #365 from phst/bug352
Use last byte of error text as numeric error code
2017-10-09 08:07:42 +03:00
Philipp Stephani
112ccbd820 Use last byte of error text as numeric error code
Fixes #352
2017-10-03 11:42:07 +02:00
Petri Lehtinen
271ffda903 Make json_equal() const-correct
Fixes #344
2017-08-19 21:10:17 +03:00
Petri Lehtinen
3e5405c39e Work around gcc's -Wimplicit-fallthrough 2017-08-19 21:09:32 +03:00
Petri Lehtinen
93e8cd7d68 Merge pull request #359 from sanjay24/master
json_dump_file API returns success even when fclose fails
2017-08-06 07:14:18 +03:00
Sanjay Kumar
0abcbce3bb json_dump_file API returns success even when fclose fails (consider disk full case). API should check the return value of fclose before returning success to its caller. fwrite may not write anything into the file, it simply returns the number of bytes written into the buffer. When disk is full and fclose is called, it results in truncation of the file (resulting in zero sized file). Since, API is returning success, its caller can't take any remedial action on its failure. 2017-08-05 23:52:49 +05:30
Petri Lehtinen
4947f9a193 Merge pull request #356 from hellojaewon/master
Docs - Fix typo
2017-07-20 22:42:29 +03:00
최재원
ad6c1e37ad Fix typo 2017-07-16 19:16:34 +09:00
Petri Lehtinen
f52c3da717 Merge pull request #339 from npmccallum/optpack
Enable optional object members in json_pack()
2017-04-19 16:17:28 +03:00
Nathaniel McCallum
28666cead0 Enable optional object/array members in json_pack() 2017-04-19 07:19:29 -04:00
Petri Lehtinen
74028ff958 Merge commit '1b8bebf0bf8f1c0c3d92faf67a830bf8448897ed' 2017-04-13 13:46:35 +03:00
Petri Lehtinen
fbf720f2c5 Allow forward declaring json_error_t values 2017-04-13 13:39:43 +03:00
Mathieu Lirzin
1b8bebf0bf build: Use Autoconf macro @includedir@ for jansson.pc.
This is more consistent with what is done for other pkg-config
variables.
2017-04-12 22:36:58 +02:00
Petri Lehtinen
f7a70de84a Merge pull request #336 from akheron/issue-333
CMakeLists.txt: Remove extra parenthesis
2017-03-29 07:31:26 +03:00
Petri Lehtinen
17f77cf2c6 CMakeLists.txt: Remove extra parenthesis
Fixes #333
2017-03-29 07:08:19 +03:00
26 changed files with 720 additions and 280 deletions

42
CHANGES
View File

@@ -1,3 +1,45 @@
Version 2.11
============
Released 2018-02-09
* New features:
- Add `json_pack()` format specifiers s*, o* and O* for values that
can be omitted if null (#339).
- Add `json_error_code()` to retrieve numeric error codes (#365, #380,
#381).
- Enable thread safety for `json_dump()` on all systems. Enable thread
safe `json_decref()` and `json_incref()` for modern compilers (#389).
- Add `json_sprintf()` and `json_vsprintf()` (#393).
* Bug Fixes:
- Fix incorrect report of success from `json_dump_file()` when an error
is returned by `fclose()` (#359).
- Make json_equal() const-correct (#344).
- Fix incomplete stealing of references by `json_pack()` (#374).
* Build:
- Work around gcc's -Wimplicit-fallthrough.
- Fix CMake detection of `sys/types.h` header (#375).
- Fix `jansson.pc` generated by CMake to be more consistent with the one
generated using GNU Autotools (#368).
* Other:
- Miscellaneous documentation fixes (#356, #378, #395).
- Remove unnecessary reference actions from parsers (#377).
Version 2.10
============

View File

@@ -86,10 +86,10 @@ endif (WIN32)
# set (JANSSON_VERSION "2.3.1")
# set (JANSSON_SOVERSION 2)
set(JANSSON_DISPLAY_VERSION "2.10")
set(JANSSON_DISPLAY_VERSION "2.11")
# This is what is required to match the same numbers as automake's
set(JANSSON_VERSION "4.10.0")
set(JANSSON_VERSION "4.11.0")
set(JANSSON_SOVERSION 4)
# for CheckFunctionKeywords
@@ -138,7 +138,7 @@ check_include_files (unistd.h HAVE_UNISTD_H)
check_include_files (sys/param.h HAVE_SYS_PARAM_H)
check_include_files (sys/stat.h HAVE_SYS_STAT_H)
check_include_files (sys/time.h HAVE_SYS_TIME_H)
check_include_files (sys/time.h HAVE_SYS_TYPES_H)
check_include_files (sys/types.h HAVE_SYS_TYPES_H)
check_function_exists (close HAVE_CLOSE)
check_function_exists (getpid HAVE_GETPID)
@@ -250,7 +250,7 @@ endif ()
# detect what to use for the 64 bit type.
# Note: I will prefer long long if I can get it, as that is what the automake system aimed for.
if (NOT DEFINED JSON_INT_T)
if (HAVE_LONG_LONG_INT AND ((${LONG_LONG_INT}) EQUAL 8))
if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8))
set (JSON_INT_T "long long")
elseif (HAVE_INT64_T)
set (JSON_INT_T int64_t)
@@ -303,8 +303,8 @@ else()
set (JSON_INLINE)
endif()
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS)
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS)
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1); return 0; } " HAVE_SYNC_BUILTINS)
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE); return 0; }" HAVE_ATOMIC_BUILTINS)
set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.")
@@ -496,6 +496,7 @@ if (NOT JANSSON_WITHOUT_TESTS)
test_object
test_pack
test_simple
test_sprintf
test_unpack)
# Doing arithmetic on void pointers is not allowed by Microsofts compiler
@@ -599,8 +600,9 @@ set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation
# (We use the same files as ./configure does, so we
# have to defined the same variables used there).
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/${JANSSON_INSTALL_LIB_DIR})
set(exec_prefix "\${prefix}")
set(libdir "\${exec_prefix}/${JANSSON_INSTALL_LIB_DIR}")
set(includedir "\${prefix}/${JANSSON_INSTALL_INCLUDE_DIR}")
set(VERSION ${JANSSON_DISPLAY_VERSION})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)

View File

@@ -1,5 +1,5 @@
AC_PREREQ([2.60])
AC_INIT([jansson], [2.10], [petri@digip.org])
AC_INIT([jansson], [2.11], [petri@digip.org])
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([1.10 foreign])
@@ -38,25 +38,33 @@ AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strto
AC_MSG_CHECKING([for gcc __sync builtins])
have_sync_builtins=no
AC_TRY_LINK(
[], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1);],
[], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1);],
[have_sync_builtins=yes],
)
if test "x$have_sync_builtins" = "xyes"; then
AC_DEFINE([HAVE_SYNC_BUILTINS], [1],
[Define to 1 if gcc's __sync builtins are available])
json_have_sync_builtins=1
else
json_have_sync_builtins=0
fi
AC_SUBST([json_have_sync_builtins])
AC_MSG_RESULT([$have_sync_builtins])
AC_MSG_CHECKING([for gcc __atomic builtins])
have_atomic_builtins=no
AC_TRY_LINK(
[], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE);],
[], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE);],
[have_atomic_builtins=yes],
)
if test "x$have_atomic_builtins" = "xyes"; then
AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1],
[Define to 1 if gcc's __atomic builtins are available])
json_have_atomic_builtins=1
else
json_have_atomic_builtins=0
fi
AC_SUBST([json_have_atomic_builtins])
AC_MSG_RESULT([$have_atomic_builtins])
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in

View File

@@ -58,6 +58,11 @@ the library:
/* Code specific to version 1.3 and above */
#endif
``JANSSON_THREAD_SAFE_REFCOUNT``
If this value is defined all read-only operations and reference counting in
Jansson are thread safe. This value is not defined for versions older than
``2.11`` or when the compiler does not provide built-in atomic functions.
Value Representation
====================
@@ -332,6 +337,8 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string`, but with explicit length, so *value* may
contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: json_t *json_string_nocheck(const char *value)
.. refcounting:: new
@@ -347,6 +354,8 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string_nocheck`, but with explicit length, so
*value* may contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: const char *json_string_value(const json_t *string)
Returns the associated value of *string* as a null terminated UTF-8
@@ -361,6 +370,8 @@ length-aware functions if you wish to embed null bytes in strings.
Returns the length of *string* in its UTF-8 presentation, or zero
if *string* is not a JSON string.
.. versionadded:: 2.7
.. function:: int json_string_set(json_t *string, const char *value)
Sets the associated value of *string* to *value*. *value* must be a
@@ -372,6 +383,8 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string_set`, but with explicit length, so *value*
may contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: int json_string_set_nocheck(json_t *string, const char *value)
Like :func:`json_string_set`, but doesn't check that *value* is
@@ -384,6 +397,18 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string_set_nocheck`, but with explicit length,
so *value* may contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: json_t *json_sprintf(const char *format, ...)
json_t *json_vsprintf(const char *format, va_list ap)
.. refcounting:: new
Construct a JSON string from a format string and varargs, just like
:func:`printf()`.
.. versionadded:: 2.11
Number
======
@@ -813,6 +838,9 @@ this struct.
The error message (in UTF-8), or an empty string if a message is
not available.
The last byte of this array contains a numeric error code. Use
:func:`json_error_code()` to extract this code.
.. member:: char source[]
Source of the error. This can be (a part of) the file name or a
@@ -855,6 +883,97 @@ success. See :ref:`apiref-decoding` for more info.
All functions also accept *NULL* as the :type:`json_error_t` pointer,
in which case no error information is returned to the caller.
.. type:: enum json_error_code
An enumeration containing numeric error codes. The following errors are
currently defined:
``json_error_unknown``
Unknown error. This should only be returned for non-errorneous
:type:`json_error_t` structures.
``json_error_out_of_memory``
The library couldnt allocate any heap memory.
``json_error_stack_overflow``
Nesting too deep.
``json_error_cannot_open_file``
Couldnt open input file.
``json_error_invalid_argument``
A function argument was invalid.
``json_error_invalid_utf8``
The input string isnt valid UTF-8.
``json_error_premature_end_of_input``
The input ended in the middle of a JSON value.
``json_error_end_of_input_expected``
There was some text after the end of a JSON value. See the
``JSON_DISABLE_EOF_CHECK`` flag.
``json_error_invalid_syntax``
JSON syntax error.
``json_error_invalid_format``
Invalid format string for packing or unpacking.
``json_error_wrong_type``
When packing or unpacking, the actual type of a value differed from the
one specified in the format string.
``json_error_null_character``
A null character was detected in a JSON string. See the
``JSON_ALLOW_NUL`` flag.
``json_error_null_value``
When packing or unpacking, some key or value was ``NULL``.
``json_error_null_byte_in_key``
An object key would contain a null byte. Jansson cant represent such
keys; see :ref:`rfc-conformance`.
``json_error_duplicate_key``
Duplicate key in object. See the ``JSON_REJECT_DUPLICATES`` flag.
``json_error_numeric_overflow``
When converting a JSON number to a C numeric type, a numeric overflow
was detected.
``json_error_item_not_found``
Key in object not found.
``json_error_index_out_of_range``
Array index is out of range.
.. versionadded:: 2.11
.. function:: enum json_error_code json_error_code(const json_error_t *error)
Returns the error code embedded in ``error->text``.
.. versionadded:: 2.11
Encoding
========
@@ -1019,6 +1138,10 @@ These functions output UTF-8:
the length of the buffer, and *data* is the corresponding
:func:`json_dump_callback()` argument passed through.
*buffer* is guaranteed to be a valid UTF-8 string (i.e. multi-byte
code unit sequences are preserved). *buffer* never contains
embedded null bytes.
On error, the function should return -1 to stop the encoding
process. On success, it should return 0.
@@ -1214,11 +1337,19 @@ If no error or position information is needed, you can pass *NULL*.
*buffer* points to a buffer of *buflen* bytes, and *data* is the
corresponding :func:`json_load_callback()` argument passed through.
On success, the function should return the number of bytes read; a
returned value of 0 indicates that no data was read and that the
end of file has been reached. On error, the function should return
On success, the function should write at most *buflen* bytes to
*buffer*, and return the number of bytes written; a returned value
of 0 indicates that no data was produced and that the end of file
has been reached. On error, the function should return
``(size_t)-1`` to abort the decoding process.
In UTF-8, some code points are encoded as multi-byte sequences. The
callback function doesn't need to worry about this, as Jansson
handles it at a higher level. For example, you can safely read a
fixed number of bytes from a network connection without having to
care about code unit sequences broken apart by the chunk
boundaries.
.. versionadded:: 2.4
.. function:: json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error)
@@ -1269,6 +1400,14 @@ arguments.
.. versionadded:: 2.8
``s*`` (string) [const char \*]
Like ``s``, but if the argument is *NULL*, do not output any value.
This format can only be used inside an object or an array. If used
inside an object, the corresponding key is additionally suppressed
when the value is omitted. See below for an example.
.. versionadded:: 2.11
``s#`` (string) [const char \*, int]
Convert a UTF-8 buffer of a given length to a JSON string.
@@ -1324,11 +1463,20 @@ arguments.
yourself.
``o?``, ``O?`` (any value) [json_t \*]
Like ``o`` and ``O?``, respectively, but if the argument is
Like ``o`` and ``O``, respectively, but if the argument is
*NULL*, output a JSON null value.
.. versionadded:: 2.8
``o*``, ``O*`` (any value) [json_t \*]
Like ``o`` and ``O``, respectively, but if the argument is
*NULL*, do not output any value. This format can only be used
inside an object or an array. If used inside an object, the
corresponding key is additionally suppressed. See below for an
example.
.. versionadded:: 2.11
``[fmt]`` (array)
Build an array with contents from the inner format string. ``fmt``
may contain objects and arrays, i.e. recursive value building is
@@ -1387,6 +1535,10 @@ More examples::
/* Concatenate strings together to build the JSON string "foobarbaz" */
json_pack("s++", "foo", "bar", "baz");
/* Create an empty object or array when optional members are missing */
json_pack("{s:s*,s:o*,s:O*}", "foo", NULL, "bar", NULL, "baz", NULL);
json_pack("[s*,o*,O*]", NULL, NULL, NULL);
.. _apiref-unpack:
@@ -1443,7 +1595,10 @@ type whose address should be passed.
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.
Like ``o``, but the JSON value's reference count is incremented.
Storage pointers should be initialized NULL before using unpack.
The caller is responsible for releasing all references incremented
by unpack, even when an error occurs.
``[fmt]`` (array)
Convert each item in the JSON array according to the inner format

View File

@@ -48,7 +48,7 @@ copyright = u'2009-2016, Petri Lehtinen'
# built documents.
#
# The short X.Y version.
version = '2.10'
version = '2.11'
# The full version, including alpha/beta/rc tags.
release = version

View File

@@ -7,29 +7,31 @@ Portability
Thread safety
-------------
Jansson is thread safe and has no mutable global state. The only
exceptions are the hash function seed and memory allocation functions,
see below.
Jansson as a library is thread safe and has no mutable global state.
The only exceptions are the hash function seed and memory allocation
functions, see below.
There's no locking performed inside Jansson's code, so a multithreaded
program must perform its own locking if JSON values are shared by
multiple threads. Jansson's reference counting semantics may make this
a bit harder than it seems, as it's possible to have a reference to a
value that's also stored inside a list or object. Modifying the
container (adding or removing values) may trigger concurrent access to
such values, as containers manage the reference count of their
contained values. Bugs involving concurrent incrementing or
decrementing of deference counts may be hard to track.
There's no locking performed inside Jansson's code. **Read-only**
access to JSON values shared by multiple threads is safe, but
**mutating** a JSON value that's shared by multiple threads is not. A
multithreaded program must perform its own locking if JSON values
shared by multiple threads are mutated.
The encoding functions (:func:`json_dumps()` and friends) track
reference loops by modifying the internal state of objects and arrays.
For this reason, encoding functions must not be run on the same JSON
values in two separate threads at the same time. As already noted
above, be especially careful if two arrays or objects share their
contained values with another array or object.
However, **reference count manipulation** (:func:`json_incref()`,
:func:`json_decref()`) is usually thread-safe, and can be performed on
JSON values that are shared among threads. The thread-safety of
reference counting can be checked with the
``JANSSON_THREAD_SAFE_REFCOUNT`` preprocessor constant. Thread-safe
reference count manipulation is achieved using compiler built-in
atomic functions, which are available in most modern compilers.
If you want to make sure that two JSON value hierarchies do not
contain shared values, use :func:`json_deep_copy()` to make copies.
If compiler support is not available (``JANSSON_THREAD_SAFE_REFCOUNT``
is not defined), it may be very difficult to ensure thread safety of
reference counting. It's possible to have a reference to a value
that's also stored inside an array or object in another thread.
Modifying the container (adding or removing values) may trigger
concurrent access to such values, as containers manage the reference
count of their contained values.
Hash function seed

View File

@@ -10,7 +10,7 @@ In this tutorial, we create a program that fetches the latest commits
of a repository in GitHub_ over the web. `GitHub API`_ uses JSON, so
the result can be parsed using Jansson.
To stick to the the scope of this tutorial, we will only cover the the
To stick to the scope of this tutorial, we will only cover the
parts of the program related to handling JSON data. For the best user
experience, the full source code is available:
:download:`github_commits.c`. To compile it (on Unix-like systems with
@@ -238,7 +238,7 @@ from a JSON string using :func:`json_string_value()`::
message_text = json_string_value(message);
printf("%.8s %.*s\n",
json_string_value(id),
json_string_value(sha),
newline_offset(message_text),
message_text);
}

View File

@@ -1,7 +1,7 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=${prefix}/include
includedir=@includedir@
Name: Jansson
Description: Library for encoding, decoding and manipulating JSON data

View File

@@ -24,4 +24,4 @@ libjansson_la_SOURCES = \
libjansson_la_LDFLAGS = \
-no-undefined \
-export-symbols-regex '^json_' \
-version-info 14:0:10
-version-info 15:0:11

View File

@@ -196,8 +196,17 @@ static int compare_keys(const void *key1, const void *key2)
return strcmp(*(const char **)key1, *(const char **)key2);
}
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
{
snprintf(key, key_size, "%p", json);
if (hashtable_get(parents, key))
return -1;
return hashtable_set(parents, key, json_null());
}
static int do_dump(const json_t *json, size_t flags, int depth,
json_dump_callback_t dump, void *data)
hashtable_t *parents, json_dump_callback_t dump, void *data)
{
int embed = flags & JSON_EMBED;
@@ -251,58 +260,53 @@ static int do_dump(const json_t *json, size_t flags, int depth,
{
size_t n;
size_t i;
json_array_t *array;
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
char key[2 + (sizeof(json) * 2) + 1];
/* detect circular references */
array = json_to_array(json);
if(array->visited)
goto array_error;
array->visited = 1;
if (loop_check(parents, json, key, sizeof(key)))
return -1;
n = json_array_size(json);
if(!embed && dump("[", 1, data))
goto array_error;
return -1;
if(n == 0) {
array->visited = 0;
hashtable_del(parents, key);
return embed ? 0 : dump("]", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
goto array_error;
return -1;
for(i = 0; i < n; ++i) {
if(do_dump(json_array_get(json, i), flags, depth + 1,
dump, data))
goto array_error;
parents, dump, data))
return -1;
if(i < n - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
goto array_error;
return -1;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
goto array_error;
return -1;
}
}
array->visited = 0;
hashtable_del(parents, key);
return embed ? 0 : dump("]", 1, data);
array_error:
array->visited = 0;
return -1;
}
case JSON_OBJECT:
{
json_object_t *object;
void *iter;
const char *separator;
int separator_length;
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
char key[2 + (sizeof(json) * 2) + 1];
if(flags & JSON_COMPACT) {
separator = ":";
@@ -314,21 +318,19 @@ static int do_dump(const json_t *json, size_t flags, int depth,
}
/* detect circular references */
object = json_to_object(json);
if(object->visited)
goto object_error;
object->visited = 1;
if (loop_check(parents, json, key, sizeof(key)))
return -1;
iter = json_object_iter((json_t *)json);
if(!embed && dump("{", 1, data))
goto object_error;
return -1;
if(!iter) {
object->visited = 0;
hashtable_del(parents, key);
return embed ? 0 : dump("}", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
goto object_error;
return -1;
if(flags & JSON_SORT_KEYS)
{
@@ -338,7 +340,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
size = json_object_size(json);
keys = jsonp_malloc(size * sizeof(const char *));
if(!keys)
goto object_error;
return -1;
i = 0;
while(iter)
@@ -362,10 +364,10 @@ static int do_dump(const json_t *json, size_t flags, int depth,
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, dump, data))
do_dump(value, flags, depth + 1, parents, dump, data))
{
jsonp_free(keys);
goto object_error;
return -1;
}
if(i < size - 1)
@@ -374,7 +376,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
dump_indent(flags, depth + 1, 1, dump, data))
{
jsonp_free(keys);
goto object_error;
return -1;
}
}
else
@@ -382,7 +384,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
if(dump_indent(flags, depth, 0, dump, data))
{
jsonp_free(keys);
goto object_error;
return -1;
}
}
}
@@ -401,31 +403,27 @@ static int do_dump(const json_t *json, size_t flags, int depth,
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1,
dump, data))
goto object_error;
parents, dump, data))
return -1;
if(next)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
goto object_error;
return -1;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
goto object_error;
return -1;
}
iter = next;
}
}
object->visited = 0;
hashtable_del(parents, key);
return embed ? 0 : dump("}", 1, data);
object_error:
object->visited = 0;
return -1;
}
default:
@@ -481,16 +479,26 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
result = json_dumpf(json, output, flags);
fclose(output);
if(fclose(output) != 0)
return -1;
return result;
}
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{
int res;
hashtable_t parents_set;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, callback, data);
if (hashtable_init(&parents_set))
return -1;
res = do_dump(json, flags, 0, &parents_set, callback, data);
hashtable_close(&parents_set);
return res;
}

View File

@@ -34,17 +34,19 @@ void jsonp_error_set_source(json_error_t *error, const char *source)
}
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, const char *msg, ...)
size_t position, enum json_error_code code,
const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
jsonp_error_vset(error, line, column, position, msg, ap);
jsonp_error_vset(error, line, column, position, code, msg, ap);
va_end(ap);
}
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap)
size_t position, enum json_error_code code,
const char *msg, va_list ap)
{
if(!error)
return;
@@ -58,6 +60,7 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
error->column = column;
error->position = (int)position;
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap);
error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0';
error->text[JSON_ERROR_TEXT_LENGTH - 1] = code;
}

View File

@@ -3,6 +3,8 @@ EXPORTS
json_true
json_false
json_null
json_sprintf
json_vsprintf
json_string
json_stringn
json_string_nocheck

View File

@@ -21,11 +21,11 @@ extern "C" {
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 10
#define JANSSON_MINOR_VERSION 11
#define JANSSON_MICRO_VERSION 0
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.10"
#define JANSSON_VERSION "2.11"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -33,6 +33,11 @@ extern "C" {
(JANSSON_MINOR_VERSION << 8) | \
(JANSSON_MICRO_VERSION << 0))
/* If __atomic or __sync builtins are available the library is thread
* safe for all read-only functions plus reference counting. */
#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS
#define JANSSON_THREAD_SAFE_REFCOUNT 1
#endif
/* types */
@@ -49,7 +54,7 @@ typedef enum {
typedef struct json_t {
json_type type;
size_t refcount;
volatile size_t refcount;
} json_t;
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
@@ -94,11 +99,23 @@ json_t *json_false(void);
#define json_boolean(val) ((val) ? json_true() : json_false())
json_t *json_null(void);
/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */
#if JSON_HAVE_ATOMIC_BUILTINS
#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE)
#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE)
#elif JSON_HAVE_SYNC_BUILTINS
#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1)
#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1)
#else
#define JSON_INTERNAL_INCREF(json) (++json->refcount)
#define JSON_INTERNAL_DECREF(json) (--json->refcount)
#endif
static JSON_INLINE
json_t *json_incref(json_t *json)
{
if(json && json->refcount != (size_t)-1)
++json->refcount;
JSON_INTERNAL_INCREF(json);
return json;
}
@@ -108,7 +125,7 @@ void json_delete(json_t *json);
static JSON_INLINE
void json_decref(json_t *json)
{
if(json && json->refcount != (size_t)-1 && --json->refcount == 0)
if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0)
json_delete(json);
}
@@ -131,7 +148,7 @@ void json_decrefp(json_t **json)
#define JSON_ERROR_TEXT_LENGTH 160
#define JSON_ERROR_SOURCE_LENGTH 80
typedef struct {
typedef struct json_error_t {
int line;
int column;
int position;
@@ -139,6 +156,30 @@ typedef struct {
char text[JSON_ERROR_TEXT_LENGTH];
} json_error_t;
enum json_error_code {
json_error_unknown,
json_error_out_of_memory,
json_error_stack_overflow,
json_error_cannot_open_file,
json_error_invalid_argument,
json_error_invalid_utf8,
json_error_premature_end_of_input,
json_error_end_of_input_expected,
json_error_invalid_syntax,
json_error_invalid_format,
json_error_wrong_type,
json_error_null_character,
json_error_null_value,
json_error_null_byte_in_key,
json_error_duplicate_key,
json_error_numeric_overflow,
json_error_item_not_found,
json_error_index_out_of_range
};
static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) {
return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1];
}
/* getters, setters, manipulation */
@@ -248,10 +289,15 @@ int json_unpack(json_t *root, const char *fmt, ...);
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
/* sprintf */
json_t *json_sprintf(const char *fmt, ...);
json_t *json_vsprintf(const char *fmt, va_list ap);
/* equality */
int json_equal(json_t *value1, json_t *value2);
int json_equal(const json_t *value1, const json_t *value2);
/* copying */

View File

@@ -36,6 +36,14 @@
otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
/* If __atomic builtins are available they will be used to manage
reference counts of json_t. */
#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@
/* If __atomic builtins are not available we try using __sync builtins
to manage reference counts of json_t. */
#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048

View File

@@ -35,7 +35,6 @@
typedef struct {
json_t json;
hashtable_t hashtable;
int visited;
} json_object_t;
typedef struct {
@@ -43,7 +42,6 @@ typedef struct {
size_t size;
size_t entries;
json_t **table;
int visited;
} json_array_t;
typedef struct {
@@ -75,9 +73,11 @@ json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
void jsonp_error_init(json_error_t *error, const char *source);
void jsonp_error_set_source(json_error_t *error, const char *source);
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, const char *msg, ...);
size_t position, enum json_error_code code,
const char *msg, ...);
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
size_t position, enum json_error_code code,
const char *msg, va_list ap);
/* Locale independent string<->double conversions */
int jsonp_strtod(strbuffer_t *strbuffer, double *out);

View File

@@ -84,6 +84,7 @@ typedef struct {
/*** error reporting ***/
static void error_set(json_error_t *error, const lex_t *lex,
enum json_error_code code,
const char *msg, ...)
{
va_list ap;
@@ -121,6 +122,10 @@ static void error_set(json_error_t *error, const lex_t *lex,
}
else
{
if(code == json_error_invalid_syntax) {
/* More specific error code for premature end of file. */
code = json_error_premature_end_of_input;
}
if(lex->stream.state == STREAM_STATE_ERROR) {
/* No context for UTF-8 decoding errors */
result = msg_text;
@@ -134,7 +139,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
}
}
jsonp_error_set(error, line, col, pos, "%s", result);
jsonp_error_set(error, line, col, pos, code, "%s", result);
}
@@ -213,7 +218,7 @@ static int stream_get(stream_t *stream, json_error_t *error)
out:
stream->state = STREAM_STATE_ERROR;
error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c);
error_set(error, stream_to_lex(stream), json_error_invalid_utf8, "unable to decode byte 0x%x", c);
return STREAM_STATE_ERROR;
}
@@ -336,7 +341,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
goto out;
else if(c == STREAM_STATE_EOF) {
error_set(error, lex, "premature end of input");
error_set(error, lex, json_error_premature_end_of_input, "premature end of input");
goto out;
}
@@ -344,9 +349,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
/* control character */
lex_unget_unsave(lex, c);
if(c == '\n')
error_set(error, lex, "unexpected newline");
error_set(error, lex, json_error_invalid_syntax, "unexpected newline");
else
error_set(error, lex, "control character 0x%x", c);
error_set(error, lex, json_error_invalid_syntax, "control character 0x%x", c);
goto out;
}
@@ -356,7 +361,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
c = lex_get_save(lex, error);
for(i = 0; i < 4; i++) {
if(!l_isxdigit(c)) {
error_set(error, lex, "invalid escape");
error_set(error, lex, json_error_invalid_syntax, "invalid escape");
goto out;
}
c = lex_get_save(lex, error);
@@ -366,7 +371,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
c == 'f' || c == 'n' || c == 'r' || c == 't')
c = lex_get_save(lex, error);
else {
error_set(error, lex, "invalid escape");
error_set(error, lex, json_error_invalid_syntax, "invalid escape");
goto out;
}
}
@@ -400,7 +405,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
value = decode_unicode_escape(p);
if(value < 0) {
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1);
goto out;
}
p += 5;
@@ -410,7 +415,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(*p == '\\' && *(p + 1) == 'u') {
int32_t value2 = decode_unicode_escape(++p);
if(value2 < 0) {
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1);
goto out;
}
p += 5;
@@ -425,6 +430,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
else {
/* invalid second surrogate */
error_set(error, lex,
json_error_invalid_syntax,
"invalid Unicode '\\u%04X\\u%04X'",
value, value2);
goto out;
@@ -432,13 +438,13 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
}
else {
/* no second surrogate */
error_set(error, lex, "invalid Unicode '\\u%04X'",
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'",
value);
goto out;
}
}
else if(0xDC00 <= value && value <= 0xDFFF) {
error_set(error, lex, "invalid Unicode '\\u%04X'", value);
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", value);
goto out;
}
@@ -526,9 +532,9 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
intval = json_strtoint(saved_text, &end, 10);
if(errno == ERANGE) {
if(intval < 0)
error_set(error, lex, "too big negative integer");
error_set(error, lex, json_error_numeric_overflow, "too big negative integer");
else
error_set(error, lex, "too big integer");
error_set(error, lex, json_error_numeric_overflow, "too big integer");
goto out;
}
@@ -570,7 +576,7 @@ 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, &doubleval)) {
error_set(error, lex, "real number overflow");
error_set(error, lex, json_error_numeric_overflow, "real number overflow");
goto out;
}
@@ -701,7 +707,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
json_t *value;
if(lex->token != TOKEN_STRING) {
error_set(error, lex, "string or '}' expected");
error_set(error, lex, json_error_invalid_syntax, "string or '}' expected");
goto error;
}
@@ -710,14 +716,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
return NULL;
if (memchr(key, '\0', len)) {
jsonp_free(key);
error_set(error, lex, "NUL byte in object key not supported");
error_set(error, lex, json_error_null_byte_in_key, "NUL byte in object key not supported");
goto error;
}
if(flags & JSON_REJECT_DUPLICATES) {
if(json_object_get(object, key)) {
jsonp_free(key);
error_set(error, lex, "duplicate object key");
error_set(error, lex, json_error_duplicate_key, "duplicate object key");
goto error;
}
}
@@ -725,7 +731,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
lex_scan(lex, error);
if(lex->token != ':') {
jsonp_free(key);
error_set(error, lex, "':' expected");
error_set(error, lex, json_error_invalid_syntax, "':' expected");
goto error;
}
@@ -736,13 +742,11 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
goto error;
}
if(json_object_set_nocheck(object, key, value)) {
if(json_object_set_new_nocheck(object, key, value)) {
jsonp_free(key);
json_decref(value);
goto error;
}
json_decref(value);
jsonp_free(key);
lex_scan(lex, error);
@@ -753,7 +757,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
}
if(lex->token != '}') {
error_set(error, lex, "'}' expected");
error_set(error, lex, json_error_invalid_syntax, "'}' expected");
goto error;
}
@@ -779,11 +783,9 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
if(!elem)
goto error;
if(json_array_append(array, elem)) {
json_decref(elem);
if(json_array_append_new(array, elem)) {
goto error;
}
json_decref(elem);
lex_scan(lex, error);
if(lex->token != ',')
@@ -793,7 +795,7 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
}
if(lex->token != ']') {
error_set(error, lex, "']' expected");
error_set(error, lex, json_error_invalid_syntax, "']' expected");
goto error;
}
@@ -810,7 +812,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
lex->depth++;
if(lex->depth > JSON_PARSER_MAX_DEPTH) {
error_set(error, lex, "maximum parsing depth reached");
error_set(error, lex, json_error_stack_overflow, "maximum parsing depth reached");
return NULL;
}
@@ -821,7 +823,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
if(!(flags & JSON_ALLOW_NUL)) {
if(memchr(value, '\0', len)) {
error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL");
error_set(error, lex, json_error_null_character, "\\u0000 is not allowed without JSON_ALLOW_NUL");
return NULL;
}
}
@@ -865,11 +867,11 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
break;
case TOKEN_INVALID:
error_set(error, lex, "invalid token");
error_set(error, lex, json_error_invalid_syntax, "invalid token");
return NULL;
default:
error_set(error, lex, "unexpected token");
error_set(error, lex, json_error_invalid_syntax, "unexpected token");
return NULL;
}
@@ -889,7 +891,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
lex_scan(lex, error);
if(!(flags & JSON_DECODE_ANY)) {
if(lex->token != '[' && lex->token != '{') {
error_set(error, lex, "'[' or '{' expected");
error_set(error, lex, json_error_invalid_syntax, "'[' or '{' expected");
return NULL;
}
}
@@ -901,7 +903,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
lex_scan(lex, error);
if(lex->token != TOKEN_EOF) {
error_set(error, lex, "end of file expected");
error_set(error, lex, json_error_end_of_input_expected, "end of file expected");
json_decref(result);
return NULL;
}
@@ -944,7 +946,7 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
jsonp_error_init(error, "<string>");
if (string == NULL) {
error_set(error, NULL, "wrong arguments");
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
return NULL;
}
@@ -988,7 +990,7 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
jsonp_error_init(error, "<buffer>");
if (buffer == NULL) {
error_set(error, NULL, "wrong arguments");
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
return NULL;
}
@@ -1019,7 +1021,7 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
jsonp_error_init(error, source);
if (input == NULL) {
error_set(error, NULL, "wrong arguments");
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
return NULL;
}
@@ -1058,7 +1060,7 @@ json_t *json_loadfd(int input, size_t flags, json_error_t *error)
jsonp_error_init(error, source);
if (input < 0) {
error_set(error, NULL, "wrong arguments");
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
return NULL;
}
@@ -1079,14 +1081,14 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
jsonp_error_init(error, path);
if (path == NULL) {
error_set(error, NULL, "wrong arguments");
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
return NULL;
}
fp = fopen(path, "rb");
if(!fp)
{
error_set(error, NULL, "unable to open %s: %s",
error_set(error, NULL, json_error_cannot_open_file, "unable to open %s: %s",
path, strerror(errno));
return NULL;
}
@@ -1139,7 +1141,7 @@ json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flag
jsonp_error_init(error, "<callback>");
if (callback == NULL) {
error_set(error, NULL, "wrong arguments");
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
return NULL;
}

View File

@@ -359,17 +359,17 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=((uint32_t)k[11])<<24;
case 11: c+=((uint32_t)k[10])<<16;
case 10: c+=((uint32_t)k[9])<<8;
case 9 : c+=k[8];
case 8 : b+=((uint32_t)k[7])<<24;
case 7 : b+=((uint32_t)k[6])<<16;
case 6 : b+=((uint32_t)k[5])<<8;
case 5 : b+=k[4];
case 4 : a+=((uint32_t)k[3])<<24;
case 3 : a+=((uint32_t)k[2])<<16;
case 2 : a+=((uint32_t)k[1])<<8;
case 12: c+=((uint32_t)k[11])<<24; /* fall through */
case 11: c+=((uint32_t)k[10])<<16; /* fall through */
case 10: c+=((uint32_t)k[9])<<8; /* fall through */
case 9 : c+=k[8]; /* fall through */
case 8 : b+=((uint32_t)k[7])<<24; /* fall through */
case 7 : b+=((uint32_t)k[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k[5])<<8; /* fall through */
case 5 : b+=k[4]; /* fall through */
case 4 : a+=((uint32_t)k[3])<<24; /* fall through */
case 3 : a+=((uint32_t)k[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k[1])<<8; /* fall through */
case 1 : a+=k[0];
break;
case 0 : return c;

View File

@@ -29,6 +29,7 @@ typedef struct {
int line;
int column;
size_t pos;
int has_error;
} scanner_t;
#define token(scanner) ((scanner)->token.token)
@@ -60,6 +61,7 @@ static void scanner_init(scanner_t *s, json_error_t *error,
s->line = 1;
s->column = 0;
s->pos = 0;
s->has_error = 0;
}
static void next_token(scanner_t *s)
@@ -105,13 +107,14 @@ static void prev_token(scanner_t *s)
s->token = s->prev_token;
}
static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
static void set_error(scanner_t *s, const char *source, enum json_error_code code,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
fmt, ap);
code, fmt, ap);
jsonp_error_set_source(s->error, source);
@@ -135,24 +138,24 @@ static char *read_string(scanner_t *s, va_list *ap,
t = token(s);
prev_token(s);
*ours = 0;
if(t != '#' && t != '%' && t != '+') {
/* Optimize the simple case */
str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", "NULL string argument");
set_error(s, "<args>", json_error_null_value, "NULL string argument");
return NULL;
}
length = strlen(str);
if(!utf8_check_string(str, length)) {
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
return NULL;
}
*out_len = length;
*ours = 0;
return (char *)str;
}
@@ -161,9 +164,8 @@ static char *read_string(scanner_t *s, va_list *ap,
while(1) {
str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", "NULL string argument");
strbuffer_close(&strbuff);
return NULL;
set_error(s, "<args>", json_error_null_value, "NULL string argument");
s->has_error = 1;
}
next_token(s);
@@ -176,13 +178,12 @@ static char *read_string(scanner_t *s, va_list *ap,
}
else {
prev_token(s);
length = strlen(str);
length = s->has_error ? 0 : strlen(str);
}
if(strbuffer_append_bytes(&strbuff, str, length) == -1) {
set_error(s, "<internal>", "Out of memory");
strbuffer_close(&strbuff);
return NULL;
if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) {
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
s->has_error = 1;
}
next_token(s);
@@ -192,12 +193,18 @@ static char *read_string(scanner_t *s, va_list *ap,
}
}
if(!utf8_check_string(strbuff.value, strbuff.length)) {
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
if(s->has_error) {
strbuffer_close(&strbuff);
return NULL;
}
if(!utf8_check_string(strbuff.value, strbuff.length)) {
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
strbuffer_close(&strbuff);
s->has_error = 1;
return NULL;
}
*out_len = strbuff.length;
*ours = 1;
return strbuffer_steal_value(&strbuff);
@@ -215,18 +222,18 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
json_t *value;
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
goto error;
}
if(token(s) != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
goto error;
}
key = read_string(s, ap, "object key", &len, &ours);
if(!key)
goto error;
if (!key)
s->has_error = 1;
next_token(s);
@@ -235,24 +242,34 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
if(ours)
jsonp_free(key);
goto error;
if(strchr("soO", token(s)) && s->next_token.token == '*') {
next_token(s);
} else {
s->has_error = 1;
}
next_token(s);
continue;
}
if(json_object_set_new_nocheck(object, key, value)) {
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
if(ours)
jsonp_free(key);
if(s->has_error)
json_decref(value);
goto error;
if(!s->has_error && json_object_set_new_nocheck(object, key, value)) {
set_error(s, "<internal>", json_error_out_of_memory, "Unable to add key \"%s\"", key);
s->has_error = 1;
}
if(ours)
jsonp_free(key);
if(strchr("soO", token(s)) && s->next_token.token == '*')
next_token(s);
next_token(s);
}
return object;
if(!s->has_error)
return object;
error:
json_decref(object);
@@ -268,22 +285,38 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
json_t *value;
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
/* Format string errors are unrecoverable. */
goto error;
}
value = pack(s, ap);
if(!value)
goto error;
if(!value) {
if(strchr("soO", token(s)) && s->next_token.token == '*') {
next_token(s);
} else {
s->has_error = 1;
}
if(json_array_append_new(array, value)) {
set_error(s, "<internal>", "Unable to append to array");
goto error;
next_token(s);
continue;
}
if(s->has_error)
json_decref(value);
if(!s->has_error && json_array_append_new(array, value)) {
set_error(s, "<internal>", json_error_out_of_memory, "Unable to append to array");
s->has_error = 1;
}
if(strchr("soO", token(s)) && s->next_token.token == '*')
next_token(s);
next_token(s);
}
return array;
if(!s->has_error)
return array;
error:
json_decref(array);
@@ -376,8 +409,9 @@ static json_t *pack(scanner_t *s, va_list *ap)
}
default:
set_error(s, "<format>", "Unexpected format character '%c'",
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
token(s));
s->has_error = 1;
return NULL;
}
}
@@ -398,12 +432,12 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
hashtable_t key_set;
if(hashtable_init(&key_set)) {
set_error(s, "<internal>", "Out of memory");
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
return -1;
}
if(root && !json_is_object(root)) {
set_error(s, "<validation>", "Expected object, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s",
type_name(root));
goto out;
}
@@ -415,13 +449,13 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
int opt = 0;
if(strict != 0) {
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
set_error(s, "<format>", json_error_invalid_format, "Expected '}' after '%c', got '%c'",
(strict == 1 ? '!' : '*'), token(s));
goto out;
}
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
goto out;
}
@@ -432,13 +466,13 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
}
if(token(s) != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
goto out;
}
key = va_arg(*ap, const char *);
if(!key) {
set_error(s, "<args>", "NULL object key");
set_error(s, "<args>", json_error_null_value, "NULL object key");
goto out;
}
@@ -456,7 +490,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
else {
value = json_object_get(root, key);
if(!value && !opt) {
set_error(s, "<validation>", "Object item not found: %s", key);
set_error(s, "<validation>", json_error_item_not_found, "Object item not found: %s", key);
goto out;
}
}
@@ -513,7 +547,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
}
}
}
set_error(s, "<validation>",
set_error(s, "<validation>", json_error_end_of_input_expected,
"%li object item(s) left unpacked: %s",
unpacked, strbuffer_value(&unrecognized_keys));
strbuffer_close(&unrecognized_keys);
@@ -534,7 +568,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
int strict = 0;
if(root && !json_is_array(root)) {
set_error(s, "<validation>", "Expected array, got %s", type_name(root));
set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s", type_name(root));
return -1;
}
next_token(s);
@@ -543,14 +577,14 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
json_t *value;
if(strict != 0) {
set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
set_error(s, "<format>", json_error_invalid_format, "Expected ']' after '%c', got '%c'",
(strict == 1 ? '!' : '*'),
token(s));
return -1;
}
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
return -1;
}
@@ -561,7 +595,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
}
if(!strchr(unpack_value_starters, token(s))) {
set_error(s, "<format>", "Unexpected format character '%c'",
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
token(s));
return -1;
}
@@ -573,7 +607,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
else {
value = json_array_get(root, i);
if(!value) {
set_error(s, "<validation>", "Array index %lu out of range",
set_error(s, "<validation>", json_error_index_out_of_range, "Array index %lu out of range",
(unsigned long)i);
return -1;
}
@@ -591,7 +625,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
if(root && strict == 1 && i != json_array_size(root)) {
long diff = (long)json_array_size(root) - (long)i;
set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
set_error(s, "<validation>", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff);
return -1;
}
@@ -610,7 +644,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 's':
if(root && !json_is_string(root)) {
set_error(s, "<validation>", "Expected string, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected string, got %s",
type_name(root));
return -1;
}
@@ -621,7 +655,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
str_target = va_arg(*ap, const char **);
if(!str_target) {
set_error(s, "<args>", "NULL string argument");
set_error(s, "<args>", json_error_null_value, "NULL string argument");
return -1;
}
@@ -630,7 +664,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
if(token(s) == '%') {
len_target = va_arg(*ap, size_t *);
if(!len_target) {
set_error(s, "<args>", "NULL string length argument");
set_error(s, "<args>", json_error_null_value, "NULL string length argument");
return -1;
}
}
@@ -647,7 +681,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'i':
if(root && !json_is_integer(root)) {
set_error(s, "<validation>", "Expected integer, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
type_name(root));
return -1;
}
@@ -662,7 +696,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'I':
if(root && !json_is_integer(root)) {
set_error(s, "<validation>", "Expected integer, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
type_name(root));
return -1;
}
@@ -677,7 +711,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'b':
if(root && !json_is_boolean(root)) {
set_error(s, "<validation>", "Expected true or false, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected true or false, got %s",
type_name(root));
return -1;
}
@@ -692,7 +726,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'f':
if(root && !json_is_real(root)) {
set_error(s, "<validation>", "Expected real, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected real, got %s",
type_name(root));
return -1;
}
@@ -707,7 +741,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'F':
if(root && !json_is_number(root)) {
set_error(s, "<validation>", "Expected real or integer, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected real or integer, got %s",
type_name(root));
return -1;
}
@@ -737,14 +771,14 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'n':
/* Never assign, just validate */
if(root && !json_is_null(root)) {
set_error(s, "<validation>", "Expected null, got %s",
set_error(s, "<validation>", json_error_wrong_type, "Expected null, got %s",
type_name(root));
return -1;
}
return 0;
default:
set_error(s, "<format>", "Unexpected format character '%c'",
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
token(s));
return -1;
}
@@ -759,7 +793,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
if(!fmt || !*fmt) {
jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
return NULL;
}
jsonp_error_init(error, NULL);
@@ -777,7 +811,11 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
next_token(&s);
if(token(&s)) {
json_decref(value);
set_error(&s, "<format>", "Garbage after format string");
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
return NULL;
}
if(s.has_error) {
json_decref(value);
return NULL;
}
@@ -816,13 +854,13 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
if(!root) {
jsonp_error_init(error, "<root>");
jsonp_error_set(error, -1, -1, 0, "NULL root value");
jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value");
return -1;
}
if(!fmt || !*fmt) {
jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
return -1;
}
jsonp_error_init(error, NULL);
@@ -839,7 +877,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
next_token(&s);
if(token(&s)) {
set_error(&s, "<format>", "Garbage after format string");
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
return -1;
}

View File

@@ -67,8 +67,6 @@ json_t *json_object(void)
return NULL;
}
object->visited = 0;
return &object->json;
}
@@ -258,7 +256,10 @@ json_t *json_object_iter_value(void *iter)
int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
{
if(!json_is_object(json) || !iter || !value)
{
json_decref(value);
return -1;
}
hashtable_iter_set(iter, value);
return 0;
@@ -272,15 +273,15 @@ void *json_object_key_to_iter(const char *key)
return hashtable_key_to_iter(key);
}
static int json_object_equal(json_t *object1, json_t *object2)
static int json_object_equal(const json_t *object1, const json_t *object2)
{
const char *key;
json_t *value1, *value2;
const json_t *value1, *value2;
if(json_object_size(object1) != json_object_size(object2))
return 0;
json_object_foreach(object1, key, value1) {
json_object_foreach((json_t *)object1, key, value1) {
value2 = json_object_get(object2, key);
if(!json_equal(value1, value2))
@@ -351,8 +352,6 @@ json_t *json_array(void)
return NULL;
}
array->visited = 0;
return &array->json;
}
@@ -581,7 +580,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;
@@ -765,7 +764,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;
@@ -788,6 +787,40 @@ static json_t *json_string_copy(const json_t *string)
return json_stringn_nocheck(s->value, s->length);
}
json_t *json_vsprintf(const char *fmt, va_list ap) {
int length;
char *buf;
va_list aq;
va_copy(aq, ap);
length = vsnprintf(NULL, 0, fmt, ap);
if (length == 0)
return json_string("");
buf = jsonp_malloc(length + 1);
if (!buf)
return NULL;
vsnprintf(buf, length + 1, fmt, aq);
if (!utf8_check_string(buf, length)) {
jsonp_free(buf);
return NULL;
}
return jsonp_stringn_nocheck_own(buf, length);
}
json_t *json_sprintf(const char *fmt, ...) {
json_t *result;
va_list ap;
va_start(ap, fmt);
result = json_vsprintf(fmt, ap);
va_end(ap);
return result;
}
/*** integer ***/
@@ -825,7 +858,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);
}
@@ -877,7 +910,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);
}
@@ -957,7 +990,7 @@ void json_delete(json_t *json)
/*** equality ***/
int json_equal(json_t *json1, json_t *json2)
int json_equal(const json_t *json1, const json_t *json2)
{
if(!json1 || !json2)
return 0;

3
test/.gitignore vendored
View File

@@ -7,14 +7,15 @@ suites/api/test_dump
suites/api/test_dump_callback
suites/api/test_equal
suites/api/test_load
suites/api/test_load_callback
suites/api/test_loadb
suites/api/test_memory_funcs
suites/api/test_number
suites/api/test_object
suites/api/test_pack
suites/api/test_simple
suites/api/test_sprintf
suites/api/test_unpack
suites/api/test_load_callback
run-suites.log
run-suites.trs
test-suite.log

View File

@@ -14,6 +14,7 @@ check_PROGRAMS = \
test_object \
test_pack \
test_simple \
test_sprintf \
test_unpack
test_array_SOURCES = test_array.c util.h
@@ -27,6 +28,7 @@ test_number_SOURCES = test_number.c util.h
test_object_SOURCES = test_object.c util.h
test_pack_SOURCES = test_pack.c util.h
test_simple_SOURCES = test_simple.c util.h
test_sprintf_SOURCES = test_sprintf.c util.h
test_unpack_SOURCES = test_unpack.c util.h
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src

View File

@@ -32,6 +32,8 @@ static void file_not_found()
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0)
fail("json_load_file returned an invalid error message");
if(json_error_code(&error) != json_error_cannot_open_file)
fail("json_load_file returned an invalid error code");
}
static void very_long_file_name() {
@@ -46,6 +48,8 @@ static void very_long_file_name() {
if (strncmp(error.source, "...aaa", 6) != 0)
fail("error source was set incorrectly");
if(json_error_code(&error) != json_error_cannot_open_file)
fail("error code was set incorrectly");
}
static void reject_duplicates()
@@ -54,7 +58,7 @@ static void reject_duplicates()
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
fail("json_loads did not detect a duplicate key");
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
check_error(json_error_duplicate_key, "duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
}
static void disable_eof_check()
@@ -66,7 +70,7 @@ static void disable_eof_check()
if(json_loads(text, 0, &error))
fail("json_loads did not detect garbage after JSON text");
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
check_error(json_error_end_of_input_expected, "end of file expected near 'garbage'", "<string>", 1, 18, 18);
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
if(!json)
@@ -137,7 +141,8 @@ static void decode_int_as_real()
big[310] = '\0';
json = json_loads(big, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
if (json || strcmp(error.text, "real number overflow") != 0)
if (json || strcmp(error.text, "real number overflow") != 0 ||
json_error_code(&error) != json_error_numeric_overflow)
fail("json_load decode int as real failed - expected overflow");
json_decref(json);
@@ -206,6 +211,26 @@ static void position()
json_decref(json);
}
static void error_code()
{
json_error_t error;
json_t *json = json_loads("[123] garbage", 0, &error);
if(json != NULL)
fail("json_loads returned not NULL");
if(strlen(error.text) >= JSON_ERROR_TEXT_LENGTH)
fail("error.text longer than expected");
if(json_error_code(&error) != json_error_end_of_input_expected)
fail("json_loads returned incorrect error code");
json = json_loads("{\"foo\": ", 0, &error);
if(json != NULL)
fail("json_loads returned not NULL");
if(strlen(error.text) >= JSON_ERROR_TEXT_LENGTH)
fail("error.text longer than expected");
if(json_error_code(&error) != json_error_premature_end_of_input)
fail("json_loads returned incorrect error code");
}
static void run_tests()
{
file_not_found();
@@ -217,4 +242,5 @@ static void run_tests()
allow_nul();
load_wrong_args();
position();
error_code();
}

View File

@@ -240,6 +240,18 @@ static void run_tests()
fail("json_pack object refcount failed");
json_decref(value);
/* object with optional members */
value = json_pack("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL);
if(value)
fail("json_pack object optional incorrectly succeeded");
value = json_pack("{s:**}", "a", NULL);
if(value)
fail("json_pack object optional invalid incorrectly succeeded");
value = json_pack("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL);
if(!json_is_object(value) || json_object_size(value) != 0)
fail("json_pack object optional failed");
json_decref(value);
/* simple array */
value = json_pack("[i,i,i]", 0, 1, 2);
if(!json_is_array(value) || json_array_size(value) != 3)
@@ -253,6 +265,18 @@ static void run_tests()
}
json_decref(value);
/* simple array with optional members */
value = json_pack("[s,o,O]", NULL, NULL, NULL);
if(value)
fail("json_pack array optional incorrectly succeeded");
value = json_pack("[**]", NULL);
if(value)
fail("json_pack array optional invalid incorrectly succeeded");
value = json_pack("[s*,o*,O*]", NULL, NULL, NULL);
if(!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack array optional failed");
json_decref(value);
/* Whitespace; regular string */
value = json_pack(" s ", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
@@ -278,78 +302,87 @@ static void run_tests()
/* newline in format string */
if(json_pack_ex(&error, 0, "{\n\n1"))
fail("json_pack failed to catch invalid format '1'");
check_error("Expected format 's', got '1'", "<format>", 3, 1, 4);
check_error(json_error_invalid_format, "Expected format 's', got '1'", "<format>", 3, 1, 4);
/* mismatched open/close array/object */
if(json_pack_ex(&error, 0, "[}"))
fail("json_pack failed to catch mismatched '}'");
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 2, 2);
if(json_pack_ex(&error, 0, "{]"))
fail("json_pack failed to catch mismatched ']'");
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Expected format 's', got ']'", "<format>", 1, 2, 2);
/* missing close array */
if(json_pack_ex(&error, 0, "["))
fail("json_pack failed to catch missing ']'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
/* missing close object */
if(json_pack_ex(&error, 0, "{"))
fail("json_pack failed to catch missing '}'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
/* garbage after format string */
if(json_pack_ex(&error, 0, "[i]a", 42))
fail("json_pack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 4, 4);
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 4, 4);
if(json_pack_ex(&error, 0, "ia", 42))
fail("json_pack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 2, 2);
/* NULL string */
if(json_pack_ex(&error, 0, "s", NULL))
fail("json_pack failed to catch null argument string");
check_error("NULL string argument", "<args>", 1, 1, 1);
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 1, 1);
/* + on its own */
if(json_pack_ex(&error, 0, "+", NULL))
fail("json_pack failed to a lone +");
check_error("Unexpected format character '+'", "<format>", 1, 1, 1);
check_error(json_error_invalid_format, "Unexpected format character '+'", "<format>", 1, 1, 1);
/* NULL format */
if(json_pack_ex(&error, 0, NULL))
fail("json_pack failed to catch NULL format string");
check_error("NULL or empty format string", "<format>", -1, -1, 0);
check_error(json_error_invalid_argument, "NULL or empty format string", "<format>", -1, -1, 0);
/* NULL key */
if(json_pack_ex(&error, 0, "{s:i}", NULL, 1))
fail("json_pack failed to catch NULL key");
check_error("NULL string argument", "<args>", 1, 2, 2);
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 2, 2);
/* NULL value followed by object still steals the object's ref */
value = json_incref(json_object());
if(json_pack_ex(&error, 0, "{s:s,s:o}", "badnull", NULL, "dontleak", value))
fail("json_pack failed to catch NULL value");
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 4, 4);
if(value->refcount != (size_t)1)
fail("json_pack failed to steal reference after error.");
json_decref(value);
/* More complicated checks for row/columns */
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
fail("json_pack failed to catch object as key");
check_error("Expected format 's', got '{'", "<format>", 1, 3, 3);
check_error(json_error_invalid_format, "Expected format 's', got '{'", "<format>", 1, 3, 3);
/* Complex object */
if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13))
fail("json_pack failed to catch missing ]");
check_error("Unexpected format character '}'", "<format>", 1, 19, 19);
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 19, 19);
/* Complex array */
if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"))
fail("json_pack failed to catch extra }");
check_error("Unexpected format character '}'", "<format>", 1, 21, 21);
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 21, 21);
/* Invalid UTF-8 in object key */
if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42))
fail("json_pack failed to catch invalid UTF-8 in an object key");
check_error("Invalid UTF-8 object key", "<args>", 1, 2, 2);
check_error(json_error_invalid_utf8, "Invalid UTF-8 object key", "<args>", 1, 2, 2);
/* Invalid UTF-8 in a string */
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in a string");
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "<args>", 1, 4, 4);
}

View File

@@ -0,0 +1,22 @@
#include <string.h>
#include <jansson.h>
#include "util.h"
static void test_sprintf() {
json_t *s = json_sprintf("foo bar %d", 42);
if (!s)
fail("json_sprintf returned NULL");
if (!json_is_string(s))
fail("json_sprintf didn't return a JSON string");
if (strcmp(json_string_value(s), "foo bar 42"))
fail("json_sprintf generated an unexpected string");
json_decref(s);
}
static void run_tests()
{
test_sprintf();
}

View File

@@ -144,65 +144,65 @@ static void run_tests()
j = json_integer(42);
if(!json_unpack_ex(j, &error, 0, "z"))
fail("json_unpack succeeded with invalid format character");
check_error("Unexpected format character 'z'", "<format>", 1, 1, 1);
check_error(json_error_invalid_format, "Unexpected format character 'z'", "<format>", 1, 1, 1);
if(!json_unpack_ex(NULL, &error, 0, "[i]"))
fail("json_unpack succeeded with NULL root");
check_error("NULL root value", "<root>", -1, -1, 0);
check_error(json_error_null_value, "NULL root value", "<root>", -1, -1, 0);
json_decref(j);
/* mismatched open/close array/object */
j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, "[}"))
fail("json_unpack failed to catch mismatched ']'");
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 2, 2);
json_decref(j);
j = json_pack("{}");
if(!json_unpack_ex(j, &error, 0, "{]"))
fail("json_unpack failed to catch mismatched '}'");
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Expected format 's', got ']'", "<format>", 1, 2, 2);
json_decref(j);
/* missing close array */
j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, "["))
fail("json_unpack failed to catch missing ']'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
json_decref(j);
/* missing close object */
j = json_pack("{}");
if(!json_unpack_ex(j, &error, 0, "{"))
fail("json_unpack failed to catch missing '}'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
json_decref(j);
/* garbage after format string */
j = json_pack("[i]", 42);
if(!json_unpack_ex(j, &error, 0, "[i]a", &i1))
fail("json_unpack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 4, 4);
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 4, 4);
json_decref(j);
j = json_integer(12345);
if(!json_unpack_ex(j, &error, 0, "ia", &i1))
fail("json_unpack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 2, 2);
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 2, 2);
json_decref(j);
/* NULL format string */
j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, NULL))
fail("json_unpack failed to catch null format string");
check_error("NULL or empty format string", "<format>", -1, -1, 0);
check_error(json_error_invalid_argument, "NULL or empty format string", "<format>", -1, -1, 0);
json_decref(j);
/* NULL string pointer */
j = json_string("foobie");
if(!json_unpack_ex(j, &error, 0, "s", NULL))
fail("json_unpack failed to catch null string pointer");
check_error("NULL string argument", "<args>", 1, 1, 1);
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 1, 1);
json_decref(j);
/* invalid types */
@@ -210,39 +210,39 @@ static void run_tests()
j2 = json_string("foo");
if(!json_unpack_ex(j, &error, 0, "s"))
fail("json_unpack failed to catch invalid type");
check_error("Expected string, got integer", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected string, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "n"))
fail("json_unpack failed to catch invalid type");
check_error("Expected null, got integer", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected null, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "b"))
fail("json_unpack failed to catch invalid type");
check_error("Expected true or false, got integer", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected true or false, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j2, &error, 0, "i"))
fail("json_unpack failed to catch invalid type");
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected integer, got string", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j2, &error, 0, "I"))
fail("json_unpack failed to catch invalid type");
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected integer, got string", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "f"))
fail("json_unpack failed to catch invalid type");
check_error("Expected real, got integer", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected real, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j2, &error, 0, "F"))
fail("json_unpack failed to catch invalid type");
check_error("Expected real or integer, got string", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected real or integer, got string", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "[i]"))
fail("json_unpack failed to catch invalid type");
check_error("Expected array, got integer", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected array, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "{si}", "foo"))
fail("json_unpack failed to catch invalid type");
check_error("Expected object, got integer", "<validation>", 1, 1, 1);
check_error(json_error_wrong_type, "Expected object, got integer", "<validation>", 1, 1, 1);
json_decref(j);
json_decref(j2);
@@ -251,21 +251,21 @@ static void run_tests()
j = json_pack("[i]", 1);
if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
fail("json_unpack failed to catch index out of array bounds");
check_error("Array index 1 out of range", "<validation>", 1, 3, 3);
check_error(json_error_index_out_of_range, "Array index 1 out of range", "<validation>", 1, 3, 3);
json_decref(j);
/* NULL object key */
j = json_pack("{si}", "foo", 42);
if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1))
fail("json_unpack failed to catch null string pointer");
check_error("NULL object key", "<args>", 1, 2, 2);
check_error(json_error_null_value, "NULL object key", "<args>", 1, 2, 2);
json_decref(j);
/* Object key not found */
j = json_pack("{si}", "foo", 42);
if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1))
fail("json_unpack failed to catch null string pointer");
check_error("Object item not found: baz", "<validation>", 1, 3, 3);
check_error(json_error_item_not_found, "Object item not found: baz", "<validation>", 1, 3, 3);
json_decref(j);
/*
@@ -281,14 +281,14 @@ static void run_tests()
j = json_pack("[iii]", 1, 2, 3);
if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2))
fail("json_unpack array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
/* Like above, but with JSON_STRICT instead of '!' format */
j = json_pack("[iii]", 1, 2, 3);
if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2))
fail("json_unpack array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 4, 4);
check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", "<validation>", 1, 4, 4);
json_decref(j);
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
@@ -306,7 +306,7 @@ static void run_tests()
"2 object item(s) left unpacked: baz, quux",
"2 object item(s) left unpacked: quux, baz"
};
check_errors(possible_errors, 2, "<validation>", 1, 10, 10);
check_errors(json_error_end_of_input_expected, possible_errors, 2, "<validation>", 1, 10, 10);
}
json_decref(j);
@@ -320,35 +320,35 @@ static void run_tests()
j = json_pack("[ii]", 1, 2);
if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2))
fail("json_unpack failed to catch ! in the middle of an array");
check_error("Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
check_error(json_error_invalid_format, "Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2))
fail("json_unpack failed to catch * in the middle of an array");
check_error("Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
check_error(json_error_invalid_format, "Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
json_decref(j);
j = json_pack("{sssi}", "foo", "bar", "baz", 42);
if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1))
fail("json_unpack failed to catch ! in the middle of an object");
check_error("Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
check_error(json_error_invalid_format, "Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1))
fail("json_unpack failed to catch ! in the middle of an object");
check_error("Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
check_error(json_error_invalid_format, "Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
json_decref(j);
/* Error in nested object */
j = json_pack("{s{snsn}}", "foo", "bar", "baz");
if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar"))
fail("json_unpack nested object with strict validation failed");
check_error("1 object item(s) left unpacked: baz", "<validation>", 1, 7, 7);
check_error(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz", "<validation>", 1, 7, 7);
json_decref(j);
/* Error in nested array */
j = json_pack("[[ii]]", 1, 2);
if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1))
fail("json_unpack nested array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
/* Optional values */
@@ -401,6 +401,6 @@ static void run_tests()
i1 = i2 = i3 = 0;
if(!json_unpack_ex(j, &error, 0, "{sis?i!}", "foo", &i1, "bar", &i2))
fail("json_unpack failed for optional values with strict mode and compensation");
check_error("1 object item(s) left unpacked: baz", "<validation>", 1, 8, 8);
check_error(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz", "<validation>", 1, 8, 8);
json_decref(j);
}

View File

@@ -30,9 +30,16 @@
} while(0)
/* Assumes json_error_t error */
#define check_errors(texts_, num_, source_, line_, column_, position_) \
#define check_errors(code_, texts_, num_, source_, \
line_, column_, position_) \
do { \
int i_, found_ = 0; \
if(json_error_code(&error) != code_) { \
failhdr; \
fprintf(stderr, "code: %d != %d\n", \
json_error_code(&error), code_); \
exit(1); \
} \
for(i_ = 0; i_ < num_; i_++) { \
if(strcmp(error.text, texts_[i_]) == 0) { \
found_ = 1; \
@@ -73,8 +80,8 @@
/* Assumes json_error_t error */
#define check_error(text_, source_, line_, column_, position_) \
check_errors(&text_, 1, source_, line_, column_, position_)
#define check_error(code_, text_, source_, line_, column_, position_) \
check_errors(code_, &text_, 1, source_, line_, column_, position_)
static void run_tests();