351 Commits
v2.7 ... master

Author SHA1 Message Date
Petri Lehtinen
e23f558007 Merge pull request #628 from neheb/patch-1
hashtable: change to ifdef
2022-08-24 17:10:48 +03:00
Rosen Penev
1894366598 hashtable: change to ifdef
All other usages use ifdef.
2022-08-22 18:13:57 -07:00
Petri Lehtinen
7e04530916 Remove the confusing vcpkg ad from README 2022-08-09 22:24:38 +03:00
Petri Lehtinen
011e625769 Merge pull request #626 from akheron/fix-example
Add a missing json_decref in the example
2022-08-09 22:05:05 +03:00
Petri Lehtinen
de5f2963ab Add a missing json_decref in the example 2022-08-09 22:00:45 +03:00
Petri Lehtinen
128e9c5f37 Merge pull request #610 from Thomas1664/master
Fix overwriting linker flags
2022-05-03 05:48:09 +03:00
Thomas Heinrichs
fe6e8eec7e Fix overwriting linker flags 2022-04-28 17:36:54 +02:00
Petri Lehtinen
addeeef408 Add some links to README 2021-11-22 16:30:12 +02:00
Petri Lehtinen
d82b436b2f Merge pull request #598 from Mephistophiles/remove_internal_strlen
Reduce strlen's in jansson internals
2021-11-21 07:38:59 +02:00
Maxim Zhukov
586b4461e6 tests: add some cases in fixed size test
Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2021-11-20 22:57:10 +03:00
Maxim Zhukov
78418c84f1 value, pack: use key length from json_object iternals
Reduce the number of strlen calls in the jansson backend.

Test on my laptop (count of the instructions)

  Before:
  ❯ valgrind --tool=callgrind ./bin/test_object
  ==3105045== Events    : Ir
  ==3105045== Collected : 441453

  After:
  ❯ valgrind --tool=callgrind ./bin/test_object
  ==3144451== Events    : Ir
  ==3144451== Collected : 440597

Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2021-11-20 22:47:14 +03:00
Petri Lehtinen
eb81670881 Create CONTRIBUTING.md 2021-09-12 18:37:43 +03:00
Petri Lehtinen
0677666f65 Fix the check-exports tests for versioned symbols 2021-09-09 21:55:27 +03:00
Petri Lehtinen
684e18c927 jansson 2.14 2021-09-09 21:18:40 +03:00
Tomasz Paweł Gajc
e950e57e13 fix --version-script for good 2021-09-06 20:02:14 +03:00
tpgxyz
0dffb4284e use --version-script in case of linkers that does not support --default-symver 2021-04-23 06:41:58 +03:00
Petri Lehtinen
2de2c3d5fc Fix and simplify coveralls reporting 2021-01-26 22:07:32 +02:00
Petri Lehtinen
2cd3126651 Add name to the fuzz tests workflow 2021-01-26 20:03:12 +02:00
Petri Lehtinen
bde3cab216 Fix GitHub actions workflow syntax 2021-01-26 19:33:16 +02:00
Petri Lehtinen
f7aee00928 Use cifuzz actions 2021-01-25 22:20:33 +02:00
Petri Lehtinen
aa52a60ca8 README: Update build badge 2021-01-14 21:12:02 +02:00
Petri Lehtinen
d8aedd3682 Delete Travis CI config 2021-01-14 21:12:02 +02:00
Petri Lehtinen
0441ccd3c6 Also build on Visual Studio 2019 on AppVeyor 2021-01-14 21:12:02 +02:00
Petri Lehtinen
0bc4325222 GitHub Actions 2021-01-14 21:12:02 +02:00
Petri Lehtinen
fd3e9e3051 doc: Hoist a section 2020-11-19 17:31:13 +02:00
Petri Lehtinen
55ea6d44dd doc: Rename "Portability" to "Thread-safety"
Fixes #545
2020-11-19 17:27:03 +02:00
Petri Lehtinen
9a0fc069bf Merge pull request #520 from Mephistophiles/getn
Add support getn, setn functions
2020-11-19 17:21:12 +02:00
Petri Lehtinen
cc318fc042 Merge pull request #555 from kiyolee/size_t-warnings
Fix size_t related MSVC compiler warnings
2020-11-15 06:32:29 +02:00
Kelvin Lee
638449c43d C11 %zd is bit overkill here. Especially requiring conditional compile. 2020-11-06 15:51:14 +11:00
Kelvin Lee
38c4b80ab7 Fix hashsize() should return size_t. 2020-11-06 08:40:20 +11:00
Kelvin Lee
ec1b6318e4 Use size_t to receive result from fread()/ftell(). 2020-11-05 22:06:38 +11:00
Kelvin Lee
1d8201c656 Print size_t properly with C11 %zd support. 2020-11-05 22:05:54 +11:00
Maxim Zhukov
0758caaac0 reuse key len from loop check for better performance
Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2020-09-01 10:22:14 +03:00
Maxim Zhukov
16a3899a9e conformance: remove U+0000 restriction for object keys
Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2020-09-01 10:22:14 +03:00
Maxim Zhukov
ca6775dee4 introduce new fixed-size key API
This commit added functions working with fixed-size strings (non null-terminated also).
It's helpful for the following cases:
* getting key from substring without copying to separate buffer (better perfomance)
* using pure UTF-8 keys for the objets
* hack: set binary structs as the keys (see test_binary_keys)

added functions:
 * json_object_getn
 * json_object_setn
 * json_object_setn_nocheck
 * json_object_setn_new
 * json_object_setn_new_nocheck
 * json_object_deln
 * json_object_iter_key_len

added iterators:
 * json_object_keylen_foreach
 * json_object_keylen_foreach_safe

Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2020-09-01 10:22:14 +03:00
Maxim Zhukov
ba4503804b pass length of the key to internal API
Use key length for internal API

Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2020-08-12 09:57:14 +03:00
Maxim Zhukov
966f9cc20a sort tests by alpha order
Signed-off-by: Maxim Zhukov <mussitantesmortem@gmail.com>
2020-08-12 09:57:07 +03:00
Petri Lehtinen
a740f15c17 Merge pull request #546 from i-ky/patch-1
Fix couple of typos
2020-08-09 10:28:12 +03:00
i-ky
601b568b8d Fix couple of typos
"This sections describes" -> "This section describes"
2020-08-09 00:19:39 +03:00
Petri Lehtinen
1112580f4a Merge pull request #544 from i-ky/patch-1
Lift "Depth of nested values" to a higher level
2020-08-08 20:25:37 +03:00
i-ky
d9c5b1d4df Lift "Depth of nested values" to a higher level
Currently this section is under "Numbers" which seems to be wrong.
2020-08-08 14:10:16 +03:00
Petri Lehtinen
a154389827 Merge pull request #543 from ploxiln/sphinx3
doc: compatibility with Sphinx-3
2020-08-08 13:15:23 +03:00
Pierce Lopez
c329fd7bc8 doc: add return types for function-like macros
The Sphinx-3.0 c:function:: directive requires the return type.

Sphinx-3.0 also adds function-like macro support to the c:macro::
directive, which Sphinx-1.x and Sphinx-2.x do not support, but it
is probably a good idea to keep compatibility with slightly older
Sphinx for now.
2020-08-07 02:35:27 -04:00
Pierce Lopez
94318890c0 doc: do not use references for standard C types or file names
Use double-backtick quoting instead. It has the same effect
(because these links had nowhere to link to) but it does not
result in loud warnings about broken references by default with Sphinx-3.x.
2020-08-07 02:34:25 -04:00
Pierce Lopez
798d40c3f3 doc: convert refcounting directive to a class
Directive functions are no longer supported in Sphinx-3.0
but directive classes have been supported since early 1.x
2020-08-07 01:54:45 -04:00
Petri Lehtinen
73ccec0601 Merge pull request #540 from smcv/default-symver
build: Add a symbol version to all exported symbols
2020-07-20 13:01:34 +03:00
Simon McVittie
ca80d5127e build: Add a symbol version to all exported symbols for glibc
The --default-symver linker option attaches a default version definition
(the SONAME) to every exported symbol. It is supported since at least
GNU binutils 2.22 in 2011 (older versions not tested).

With this version definition, newly-linked binaries that depend on the
jansson shared library will refer to its symbols in a versioned form,
preventing their references from being resolved to a symbol of the same
name exported by json-c or json-glib if those libraries appear in
dependency search order before jansson, which will usually result in
a crash. This is necessary because ELF symbol resolution normally uses
a single flat namespace, not a tree like Windows symbol resolution.
At least one symbol (json_object_iter_next()) is exported by all three
JSON libraries.

Linking with -Bsymbolic is not enough to have this effect in all cases,
because -Bsymbolic only affects symbol lookup within a shared object,
for example when parse_json() calls json_decref(). It does not affect
calls from external code into jansson, unless jansson was statically
linked into the external caller.

This change will also not prevent code that depends on json-c or
json-glib from finding jansson's symbols and crashing; to prevent
that, a corresponding change in json-c or json-glib would be needed.

Adding a symbol-version is a backwards-compatible change, but once
added, removing or changing the symbol-version would be an incompatible
change that requires a SONAME bump.

Resolves: https://github.com/akheron/jansson/issues/523
(when combined with an equivalent change to json-c).

Signed-off-by: Simon McVittie <smcv@collabora.com>
2020-07-02 09:54:40 +01:00
Petri Lehtinen
52dfc3dd4a Merge pull request #537 from stoeckmann/vsnprintf
Handle vsnprintf corner cases.
2020-05-23 20:42:25 +03:00
Tobias Stoeckmann
38b001edbd Handle vsnprintf corner cases.
The function vsnprintf returns a negative value on error, e.g. on
an invalid format. It's best to return NULL in such a case.

Also avoid a signed integer overflow if vsnprintf returns INT_MAX.
This is undefined behaviour in C and has to be avoided.

A negative value is returned with a call like:
json_sprintf("%111111111111111s", "", "");

INT_MAX is returned with a call like:
json_sprintf("%647s%2147483000s", "", "");
2020-05-23 12:34:40 +02:00
Petri Lehtinen
e9ebfa7e77 jansson 2.13.1 2020-05-07 21:38:59 +03:00
Petri Lehtinen
3f4d948652 Merge pull request #535 from xry111/clang-format-check-dist-fix
add `scripts` directory to EXTRA_DIST
2020-05-07 21:29:30 +03:00
Petri Lehtinen
a839d3cad6 Merge pull request #534 from xry111/shared_lib_interface_fix
add jansson_* to shared library exports
2020-05-07 21:28:47 +03:00
Xℹ Ruoyao
2d2efde34a add scripts directory to EXTRA_DIST 2020-05-06 18:01:19 +08:00
Xℹ Ruoyao
bcb6b6f3fd add jansson_* to shared library exports 2020-05-06 17:31:48 +08:00
Petri Lehtinen
2882ead5bb jansson 2.13 2020-05-05 07:07:16 +03:00
Petri Lehtinen
4c9018cc4c Merge pull request #521 from NanXiao/patch-1
Update apiref.rst
2020-01-21 10:15:01 +02:00
Nan Xiao
e15fd861e1 Update apiref.rst 2020-01-21 14:27:52 +08:00
Petri Lehtinen
ca88677bdd Merge pull request #510 from AllenX2018/improve-build-doc
add MinGW build guideline and tips to disable building docs
2019-10-24 11:23:00 +03:00
Petri Lehtinen
6e211b24fe Fix the length of a section heading 2019-10-24 11:22:46 +03:00
Petri Lehtinen
50c9623e37 Merge pull request #509 from AllenX2018/update-CHANGES
add changes since v2.12
2019-10-24 11:19:52 +03:00
Petri Lehtinen
3704e1cd78 Small modifications to CHANGES 2019-10-24 11:19:34 +03:00
allen
67900c85d3 add changes since v2.12 2019-10-22 18:52:55 +08:00
Petri Lehtinen
d288cc157e Merge pull request #508 from akheron/clang-format
Format code with clang-format
2019-10-21 11:07:53 +03:00
AllenX2018
5b8ff675b3 add MinGW build guideline, add new version of Visual Studio 2019-10-21 15:53:33 +08:00
Petri Lehtinen
78da35682a Set autoconf bug-report param to point to github issues 2019-10-21 08:56:03 +03:00
Petri Lehtinen
bb4f99e919 Fix clang-format version checking 2019-10-21 08:56:03 +03:00
Petri Lehtinen
f912430cda Run clang-format with make check 2019-10-21 08:56:03 +03:00
Petri Lehtinen
a8f5fa5f5a Tweak clang-format configuration a bit
Set ColumnLimit to 90, remove AllowShortCaseLabelsOnASingleLine.
2019-10-21 08:56:03 +03:00
Petri Lehtinen
7c0297abe8 Support running a specific clang-format version in CI 2019-10-21 08:55:01 +03:00
Petri Lehtinen
1a95a60f80 Run clang-format in CI, fail the build if everything's not formatted 2019-10-21 08:55:01 +03:00
Petri Lehtinen
be0fca914e Add scripts for running clang-format 2019-10-21 08:55:01 +03:00
Petri Lehtinen
79fe8c3435 Run clang-format on *.c, *.h 2019-10-21 08:55:01 +03:00
Petri Lehtinen
7dc463ee4e Add clang-format configuration 2019-10-21 08:53:03 +03:00
Petri Lehtinen
79075d5393 Merge pull request #498 from AllenX2018/fix-issue441
fix issue #441: remove strtod definition in strconv.c
2019-10-21 08:51:36 +03:00
willson-chen
c3958a326c remove undef __NO_ISOCEXT for strtod compatiblity 2019-10-21 11:13:39 +08:00
Petri Lehtinen
f75dc840e4 Merge pull request #499 from AllenX2018/fix-issue281
Add json_object_update_new, json_object_update_existing_new and json_object_update_missing_new
2019-10-18 16:18:28 +03:00
Petri Lehtinen
25e706cce7 Merge branch 'master' into fix-issue281 2019-10-17 13:37:33 +03:00
Petri Lehtinen
8b022dad76 Merge pull request #505 from AllenX2018/recursive_object_update
Recursive object update
2019-10-17 08:36:28 +03:00
Petri Lehtinen
0eec0327be Merge pull request #500 from AllenX2018/fix-issue426
fix issue #426: fix pipe function in MinGW
2019-10-17 08:12:42 +03:00
Petri Lehtinen
e7bed4c109 Merge pull request #501 from AllenX2018/fix-issue481
fix issue #481
2019-10-17 08:11:19 +03:00
Petri Lehtinen
355aa42b48 Merge pull request #502 from AllenX2018/fix-issue453
fix issue #453:update FindSphinx.cmake to better align with find sphinx package.
2019-10-17 08:09:20 +03:00
Petri Lehtinen
92873e9d27 Merge pull request #503 from AllenX2018/loadfd-doc
add note for non-blocking file descriptor used for json_loadfd
2019-10-17 08:07:02 +03:00
Petri Lehtinen
9e1c37c889 Merge pull request #506 from AllenX2018/add-json_equal-test-case
fix typo & add negative test case for test_equal_complex
2019-10-17 07:33:36 +03:00
allen
010092c7bd fix typo & add negative test case for test_equal_complex 2019-10-15 17:41:57 +08:00
allen
fb602f331b update the test case of json_object_update_recursive 2019-10-14 17:32:24 +08:00
allen
00d2d274bc add loop check for json_object_update_recursive function 2019-10-12 15:36:05 +08:00
allen
b403bbba8d modify some words 2019-10-12 10:56:23 +08:00
Henrique Cabral
cb4727c4a9 Add json_object_update_recursive()
Support merging values nested within objects. For instance, merging:

    {
	"foo": 1,
	"bar": {
	    "baz": 2
	}
    }

with

    {
	"bar": {
	    "baz": 3
	}
    }

results in

    {
	"foo": 1,
	"bar": {
	    "baz": 3
	}
    }

instead of overwriting the value for the bar key.
2019-10-11 11:20:25 +08:00
allen
a585a2778e add note for non-blocking file descriptor used for json_loadfd 2019-10-10 19:43:17 +08:00
AllenX2018
4f26548e3a fix issue #453 2019-10-10 16:16:10 +08:00
allen
1f889c4b60 fix issue #481 2019-10-09 17:12:04 +08:00
allen
8682f2aab3 fix issue #281 2019-10-08 20:32:27 +08:00
AllenX2018
4f3305c506 fix issue #441
It already has "strtod" function in stdlib.h and because __USE_MINGW_ANSI_STDIO is desperated
2019-10-08 19:16:50 +08:00
AllenX2018
78ea35c8e9 fix issue #426 2019-10-08 16:19:40 +08:00
Petri Lehtinen
c436bec682 Merge pull request #496 from grdowns/vcpkg-instructions
Add vcpkg installation instructions
2019-09-27 15:08:00 +03:00
grdowns
f5c0afecdb Add vcpkg installation instructions 2019-09-26 16:54:02 -07:00
Petri Lehtinen
5371816480 Merge pull request #494 from luzpaz/typos
Fix various typos
2019-09-23 11:31:23 +03:00
Petri Lehtinen
09e455275c Merge pull request #490 from AllenX2018/master
add loop check in json deep copy #457
2019-09-23 11:30:33 +03:00
luz.paz
3adf3e6a5a Fix various typos
Found via `codespell -q 2` (v1.17.0.dev0)
2019-09-20 13:35:56 -04:00
Allen
672b6df474 Update apiref.rst
fix the documentation about json_dumps. refs #471
2019-09-18 16:18:51 +08:00
allen
53e9dd848f add the loop key length macro for circular reference detect 2019-09-16 15:50:59 +08:00
allen
63fb81faa5 add loop check in json deep copy #457 2019-08-12 11:17:25 +08:00
Corey Farrell
a1f297aa83 Merge pull request #489 from AllenX2018/master
Update CMakeLists.txt
2019-08-09 04:37:06 -04:00
Allen
e68c5ea0b5 Update CMakeLists.txt
add "-Wno-format-truncation" to supress format truncation warning in cmake
2019-08-09 15:41:57 +08:00
Corey Farrell
af66d3f4c0 Merge pull request #487 from cmeister2/cmeister2/ossfuzzcorpus
ossfuzz: Zip up all "input" files to use as a seed corpus
2019-07-26 09:43:44 -04:00
Corey Farrell
a6f6ec1856 Merge pull request #485 from cmeister2/cmeister2/fuzzerdiags
ossfuzz: Improvements to json_load_dump_fuzzer
2019-07-26 08:58:16 -04:00
Max Dymond
287b5acb07 ossfuzz: Zip up all "input" files to use as a seed corpus 2019-07-24 19:41:45 +01:00
Max Dymond
3858607be0 Depending on the dump_mode byte, dump out as a string or as a callback. 2019-07-24 19:14:17 +01:00
Corey Farrell
0ed750a7ea Merge pull request #486 from cmeister2/cmeister2/osstravis
Ensure OSS fuzz travis builds work for PRs
2019-07-24 13:46:24 -04:00
Max Dymond
7feae084b1 Use the correct branch for OSS Fuzz as well. 2019-07-24 10:50:21 +01:00
Max Dymond
a6f436fefc Ensure OSS fuzz travis builds work for PRs
Use the correct repository for cloning when doing a push build or a
pull-request.

Only do one fuzzer build!
2019-07-24 10:43:03 +01:00
Max Dymond
66dec35c8c ossfuzz: Improvements to json_load_dump_fuzzer
Add in diagnostics to json_load_dump_fuzzer so that if FUZZ_VERBOSE is
defined in the environment, additional debug information about the
current test case is output to stderr.

Replace json_dumps by json_dump_callback so that memory is not exhausted
by excessively nested json objects.
2019-07-24 10:17:19 +01:00
Corey Farrell
1ac79b2558 Merge pull request #484 from cmeister2/cmeister2/fixossafl
One byte fix to fix OSS Fuzz AFL support
2019-07-22 06:40:34 -04:00
Max Dymond
6e26599980 One byte fix to fix OSS Fuzz AFL support
"x$LIB_FUZZING_ENGINE" is not the correct filename to test for - remove
the x!
2019-07-22 11:23:54 +01:00
Corey Farrell
3bfa3f1946 Merge pull request #480 from cmeister2/cmeister2/ossfuzz
oss-fuzz: Create a fuzzing target for jansson
2019-07-22 05:29:22 -04:00
Corey Farrell
813c3e2c47 Merge pull request #483 from AllenX2018/master
correct spelling mistake
2019-07-22 04:19:26 -04:00
allen
c49fbc7082 correct spelling mistake 2019-07-22 11:00:31 +08:00
Max Dymond
24cc9dd078 Move ossfuzz directory and use Makefile.am 2019-07-19 09:52:37 +01:00
Max Dymond
100e5549b6 Create a fuzzing target for jansson 2019-06-28 14:05:39 +01:00
Corey Farrell
b333f3656d Merge pull request #465 from seanbright/jansson-version
Add runtime version checking functions
2019-03-13 08:24:41 -04:00
Sean Bright
76300601d9 Add runtime version checking functions
This patch adds two new exported functions:

* `jansson_version_str` - Returns a human-readable version number
* `jansson_version_cmp` - Returns an integer less than, equal to, or greater
  than zero if the runtime version of Jansson is found, respectively, to be
  less than, to match, or be greater than the provided major, minor, and micro.
2019-03-12 13:03:34 -04:00
Petri Lehtinen
f4498d2856 CHANGES: Fix 2.12 release date 2018-11-26 10:59:06 +02:00
Petri Lehtinen
71c4e8ec21 Merge pull request #448 from coreyfarrell/2.12
jansson 2.12
2018-11-26 09:10:04 +01:00
Corey Farrell
10afd33efb jansson 2.12 2018-11-25 03:40:56 -05:00
Corey Farrell
2a31c4f475 Merge pull request #447 from vsoch/update/install-readme
Update/install readme
2018-11-25 02:40:09 -05:00
vsoch
f44c137a84 extra comma! 2018-11-24 23:29:14 -08:00
vsoch
7197810714 small updates to README 2018-11-24 23:27:23 -08:00
Corey Farrell
4ba5c7cc5d Merge pull request #445 from coreyfarrell/fix-444
Remove inappropriate jsonp_free which caused segmentation fault.
2018-11-08 16:00:32 -05:00
Corey Farrell
e262ea5fcd Remove inappropriate jsonp_free which caused segmentation fault.
pack_string should never free str on error.  This wouldn't be a problem
except the check for `ours` was inverted.  Just remove the check for
ours since the true condition is unreachable.

json_vpack_ex also had an error check for s.has_error.  This can never
be true unless value is NULL.

Expand pack_unpack testing to cover empty format string, NULL object
value with non-null concatenated key, array containing a non-null object
after a NULL (error) string.

Fixes #444
2018-11-05 16:49:22 -05:00
Corey Farrell
6ac0eefed0 Merge pull request #436 from edgale/warnings
removed various warnings
2018-10-17 06:29:47 -04:00
Maximilian Röhrle
226b34d546 fixed extra bracket 2018-10-15 07:05:02 +02:00
Maximilian Röhrle
d115953347 removed explicit casts 2018-10-15 06:58:19 +02:00
Corey Farrell
15f77c8f47 Merge pull request #439 from coreyfarrell/codecoverage
Fix code coverage ignored paths.
2018-09-29 15:37:56 -04:00
Corey Farrell
ef080d17b5 Fix code coverage ignored paths.
* Replace 'tests/*' with '*/test/*'.
* Replace '/usr/*' with '/usr/include/*'.  This resolves an issue where
  it was impossible to test code coverage with the source in /usr/src.
* Ignore build/include/jansson.h as we just want src/jansson.h.
2018-09-29 14:36:54 -04:00
Corey Farrell
4f91b1d072 Merge pull request #438 from coreyfarrell/issue-437
json_pack: Improve handling of formats with '?' and '*'.
2018-09-28 14:47:33 -04:00
Corey Farrell
8d659113d5 More work on json_pack error reporting.
* Remove errant line-feed from pack_object error message.
* Correct error message in pack_object_inter.
* Create pack_integer / pack_real to get the correct error messages on
  failure when packing numeric values.
* Add tests for packing NAN and infinity directly, in an array and as
  an object value.
2018-09-25 18:03:06 -04:00
Corey Farrell
5df5fc5b13 json_pack: Improve handling of formats with '?' and '*'.
When NULL is received for an optional argument we should not set an
error message as this would block later error messages. If NULL is
received for a non-optional string we should set has_error. Set
has_error for UTF-8 errors to ensure optional strings with UTF-8
errors are not replaced with json_null(). Use 'purpose' argument in
NULL error messages of read_string.

Add error handling and tests for invalid formats where '+', '#', or '%'
is used on an optional string 's?' or 's*'.

Fix NULL string error messages to use 'purpose'.

Refactor skipping of '*' token, this is now handled by read_string and
pack_object_inter. This allows invalid format strings such as 's*#' and
's*+' to produce error messages.

Fixes #437
2018-09-25 16:35:19 -04:00
Petri Lehtinen
e65a490c44 Merge pull request #435 from edgale/master
unreferenced variable fix
2018-09-04 06:25:45 +03:00
Maximilian Röhrle
fc591913ac removed various warnings 2018-08-31 09:01:36 +02:00
Maximilian Röhrle
d83d3d9172 This fixes https://github.com/akheron/jansson/issues/434 2018-08-30 14:45:28 +02:00
Petri Lehtinen
020cc26b5c Rename a varialble that shadows another one
Fixes #430
2018-08-12 18:25:51 +03:00
Michal Privoznik
360b1ef5a1 Enable -Bsymbolic-functions linker flag whenever possible
It was discovered fairly recently that JSON parsing libraries use
common pattern to name their exported symbols (they all use
json_ prefix). So eventually it happens that two symbols from two
different libraries have the same name. This will lead to cryptic
crashes (see [1] and [2]). Linking with -Bsymbolic-functions
prevents this.

1: https://gitlab.gnome.org/GNOME/json-glib/issues/33
2: https://groups.google.com/forum/#!topic/jansson-users/7Efx-RI45IU

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2018-08-09 07:54:26 +03:00
Petri Lehtinen
37147b8a23 Merge pull request #423 from coreyfarrell/gcc-8
Deal with warnings under gcc 8.
2018-08-02 11:26:11 +03:00
Petri Lehtinen
8a00a56ec4 Merge pull request #424 from Crayon2000/patch-1
Update copyright for 2018
2018-07-30 13:42:05 +03:00
Petri Lehtinen
401ece058d Merge pull request #427 from lxin/va_end
Call va_end after va_copy in json_vsprintf
2018-07-30 13:41:39 +03:00
Xin Long
66e4ee795d Call va_end after va_copy in json_vsprintf
As said in man doc:
  "Each  invocation  of va_copy() must be matched by a corresponding
   invocation of va_end() in the same function."

va_copy may alloc memory in some system, it's necessay to free it by
va_end.

Fixes: efe6c7b3f2 ("Add json_sprintf and json_vsprintf")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
2018-07-25 17:44:00 +08:00
Crayon
3e13f514ce Update copyright for 2018 2018-07-14 16:34:33 -04:00
Corey Farrell
81fe13eeed Deal with warnings under gcc 8.
Recent versions of gcc have introduced compiler warnings for string
operations that could be truncated.  This caused problems with -Werror.
src/error.c used strncpy to write "..." to a string, but skipped writing
the NUL terminator.  Switch this to use memcpy.  src/load.c produced
warnings from snprintf writing error strings that could be truncated.
Added code to autotools build to detect `-Wno-format-truncation', add it
to AM_CFLAGS if supported.
2018-07-14 13:24:55 -04:00
Petri Lehtinen
aed855e692 Avoid invalid memory read in json_pack()
Initial patch by @bharjoc-bitdefender

Fixes #421
2018-07-09 22:26:35 +03:00
Petri Lehtinen
b59ac57617 Merge pull request #415 from palmerc/master
CMake variable to number comparison changed to provide meaningful error
2018-05-22 07:38:17 +03:00
Cameron Lowell Palmer
904f5c28ac CMake variable to number comparison changed to provide meaningful error
message.  Explicitly specify STATIC and PIC
2018-05-18 10:35:05 +02:00
Petri Lehtinen
80cea73bf9 Merge pull request #408 from isaachier/cmake
CMake improvements, make package relocatable
2018-05-18 08:08:32 +03:00
Petri Lehtinen
e28bcfeac8 Merge pull request #417 from akheron/visual-studio-15
Appveyor & build instructions for Visual Studio 15 2017
2018-05-07 14:22:03 +03:00
Petri Lehtinen
fda9288b5f Update build instructions for Visual Studio 2018-05-07 11:17:08 +03:00
Petri Lehtinen
9ff08f6312 Also build with VS 2017 in appveyor 2018-05-07 08:39:49 +03:00
Petri Lehtinen
dd4743a51d Merge pull request #412 from Mephistophiles/fix_defines
Fix JSON_HAVE_SYNC_BUILTINS define usage
2018-05-07 08:13:21 +03:00
Maxim Zhukov
50f29f9b1a Add JSON_HAVE_SYNC_BUILTINS and JSON_HAVE_ATOMIC_BUILTINS for autoheader
Added JSON_HAVE_SYNC_BUILTINS and JSON_HAVE_ATOMIC_BUILTINS defines to
jansson_config.h.cmake as well as in the autoheader for autoconf.
2018-03-22 17:46:29 +03:00
Petri Lehtinen
e5dbe7bb64 Merge pull request #409 from Mephistophiles/master
Fix error handling in json_pack
2018-03-22 10:40:41 +02:00
Maxim Zhukov
15105b66b4 Fix error handling in json_pack
Fixed a bug where the error message was not filled if an empty object
was passed to the json_pack.

Fixes #271
2018-03-22 11:16:40 +03:00
Petri Lehtinen
b23025d72b Merge pull request #404 from coreyfarrell/func-attrs
Build: Add JANSSON_ATTRS macro.
2018-03-20 04:55:48 +02:00
Isaac Hier
2c98c30a02 Stop using absolute paths 2018-03-11 10:54:24 -04:00
Isaac Hier
bb71db204f Fix output file name for version config 2018-03-11 10:54:14 -04:00
Isaac Hier
92760bb363 Remove warning 2018-03-11 10:54:02 -04:00
Isaac Hier
fe7873e963 Fix package version config generation 2018-03-11 10:37:15 -04:00
Isaac Hier
a586c0654f Fix CMake include 2018-03-11 10:27:48 -04:00
Isaac Hier
6d7a02beb0 Clean up CMake install 2018-03-11 10:24:27 -04:00
Isaac Hier
b70364b362 Remove absolute path references 2018-03-11 10:11:09 -04:00
Isaac Hier
6d1ae86e1c Fix config file 2018-03-11 09:59:52 -04:00
Isaac Hier
a324d18940 Rename target file 2018-03-11 09:53:06 -04:00
Isaac Hier
44f6606df8 Rename config files 2018-03-11 09:47:29 -04:00
Isaac Hier
d8798468c6 Upgrade CMake to 3.1 for Hunter 2018-03-11 09:34:45 -04:00
Corey Farrell
749bef0b6a More test coverage.
* Add test_load_callback to CMakeList.txt
* Add json_dump, json_load and json_unpack chaos testing.
2018-03-06 23:28:14 -05:00
Corey Farrell
e37e52549f Add warn_unused_result to strbuffer_init.
This adds a compiler warning when strbuffer_init return value is
ignored.  unpack_object is updated to deal with errors produced
while building unrecognized_keys.
2018-03-06 08:27:20 -05:00
Corey Farrell
ea664722d4 Build: Add JANSSON_ATTRS macro.
This macro is used to conditionally generate GCC/CLANG __attribute__
declarations if supported.

This allows the compiler to produce warnings on certain incorrect
usages.  json_sprintf and json_vsprintf will produce warnings on invalid
format string.  Many functions will produce a warning if the result is
unused.  Specifically functions which allocate new objects will warn if
the result is ignored as this always results in a memory leak.
2018-03-06 07:25:16 -05:00
Petri Lehtinen
d098c0ff86 Merge pull request #400 from coreyfarrell/branch-coverage
Enable branch coverage reporting.
2018-02-20 15:39:36 +02:00
Petri Lehtinen
2d494c169f Merge pull request #398 from coreyfarrell/test-coverage
Improve test coverage.
2018-02-20 15:39:19 +02:00
Corey Farrell
a5af280bac Enable branch coverage reporting. 2018-02-20 03:09:03 -05:00
Corey Farrell
73c22de516 Improve test coverage.
* Test equality of different length strings.
* Add tab to json_pack whitespace test.
* Test json_sprintf with empty result and invalid UTF.
* Test json_get_alloc_funcs with NULL arguments.
* Test invalid arguments.
* Add test_chaos to test allocation failure code paths.
* Remove redundant json_is_string checks from json_string_equal and
  json_string_copy.  Both functions are static and can only be called
  with a json string.

Fixes to issues found by test_chaos:
* Fix crash on OOM in pack_unpack.c:read_string().
* Unconditionally free string in string_create upon allocation failure.
  Update load.c:parse_value() to reflect this.  This resolves a leak on
  allocation failure for pack_unpack.c:pack_string() and
  value.c:json_sprintf().

Although not visible from CodeCoverage these changes significantly
increase branch coverage.  Especially in src/value.c where we previously
covered 67.4% of branches and now cover 96.3% of branches.
2018-02-15 10:12:31 -05:00
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
Nathaniel McCallum
b23201bb1a jansson 2.10 2017-03-02 20:49:42 +02:00
Petri Lehtinen
df454e3cf0 Merge pull request #329 from npmccallum/embed
Add JSON_EMBED encoding flag
2017-03-02 07:55:50 +02:00
Nathaniel McCallum
b8bb078cc2 Add JSON_EMBED encoding flag
The JSON_EMBED encoding flag causes the opening and closing characters
of the top-level array ('[', ']') or object ('{', '}') to be omitted
during encoding. This feature makes it possible to concatenate multiple
arrays or objects in the stream output. It also makes it possible to
perform outputs of partial composes.

One such example of a partial compose is when outputting a JWE object.
The output is a JSON object. But it has one top-level attribute
("ciphertext") that can grow out of proportion with the rest of the
metadata. With the JSON_EMBED flag, the other metadata can be composed
ahead of time and dumped during the beginning of output, where the
"ciphertext" and "tag" attributes can be streamed out in chunks. Thus,
the header material can be composed with Jansson and the ciphertext
itself can be composed manually.
2017-02-27 15:09:03 -05:00
Petri Lehtinen
3c51112063 Merge pull request #328 from npmccallum/master
Helper functions for network IO
2017-01-31 08:45:40 +02:00
Nathaniel McCallum
1672bb5a65 Implement json_dumpfd() and json_loadfd()
The primary use of these functions is easy loading
and dumping from stream sockets.

Signed-off-by: Nathaniel McCallum <npmccallum@redhat.com>
2017-01-27 09:15:18 +01:00
Nathaniel McCallum
b900967f6f Implement json_dumpb()
This function encodes the json_t object to a pre-allocated buffer.
It compliments the already existing json_loadb() function and is
useful for parsing JSON-RPC (among other protocols) when sent over
datagram sockets.

Signed-off-by: Nathaniel McCallum <npmccallum@redhat.com>
2017-01-26 16:16:24 +01:00
Petri Lehtinen
746c2c3a99 Merge pull request #321 from quiet/master
parens for LONG_LONG_INT in cmake
2016-11-16 07:11:03 +02:00
Brian Armstrong
2af820fb99 parens for LONG_LONG_INT in cmake
when this is defined as "", cmake bails with an error about arguments in
if
2016-11-12 18:58:38 -08:00
Petri Lehtinen
bc5741fb1a Merge pull request #314 from martinlindhe/condition-always-true
part of conditional expression is always true (!done)
2016-10-24 21:47:49 +03:00
Petri Lehtinen
575f951b3e Merge pull request #311 from robertmu/master
Fix passing redundant argument to error_set()
2016-10-24 21:45:50 +03:00
Petri Lehtinen
0cac862bbc Merge pull request #309 from halfaleague/larger-json-buffers
Allow parsing of buffers larger than 2GB on most 64 bit arch.
2016-10-24 21:42:30 +03:00
Martin Lindhe
4467bf243f part of conditional expression is always true (!done), found with pvs-studio 2016-10-19 16:39:26 +02:00
Robert Mu
ddd1e1f223 Fix passing redundant argument to error_set() 2016-10-07 22:11:19 +08:00
Luke Carmichael
d1e97737d6 Allow parsing of buffers larger than 2GB on most 64 bit arch.
size_t is usually 64 bits on most architectures -- this allows for larger .json files
2016-10-01 22:06:34 -04:00
Petri Lehtinen
98be7da3e2 doc: Change version to 2.10-dev 2016-09-18 14:52:26 +03:00
Petri Lehtinen
08cb7b6d6f doc: Change version to 2.9 2016-09-18 14:52:17 +03:00
Petri Lehtinen
b02db47881 jansson 2.9 2016-09-18 14:35:05 +03:00
Petri Lehtinen
074bb3838f Update copyrights for 2016 2016-09-18 14:17:03 +03:00
Petri Lehtinen
3ba3b23fdc Merge branch '2.8' 2016-09-16 08:05:48 +03:00
Matthew Johnston
e9fcab08fb Add install targets to be optional 2016-09-16 08:05:13 +03:00
Yuriy Romanenko
bdaf7584db Added test files to .gitignore 2016-09-16 08:05:13 +03:00
Petri Lehtinen
889280c976 Don't include jansson_config.h in dist tarballs
Fixes #306
2016-09-16 08:02:26 +03:00
Petri Lehtinen
f9e7aa5eeb Merge pull request #305 from warmwaffles/master
Allow install targets to be optionally defined
2016-09-12 07:46:52 +03:00
Matthew Johnston
9258671924 Add install targets to be optional 2016-09-11 00:04:49 -05:00
Petri Lehtinen
a2bbb44d96 Merge pull request #303 from groman2/yr-dev
Added test files to .gitignore
2016-09-08 07:59:04 +03:00
Yuriy Romanenko
16b516f976 Added test files to .gitignore 2016-09-07 10:13:52 -07:00
Petri Lehtinen
86196250b8 Merge branch '2.8' 2016-09-01 07:07:57 +03:00
Petri Lehtinen
ada5372cff Fix another typo 2016-09-01 07:06:25 +03:00
Petri Lehtinen
f11c1b9466 Merge pull request #302 from yujunz/patch-1
Fix typo
2016-09-01 07:05:20 +03:00
Yujun Zhang
811a30691e Fix typo 2016-09-01 08:44:08 +08:00
Petri Lehtinen
7d1af52ab4 Merge pull request #301 from npmccallum/master
Add support for the cleanup attribute in GCC/Clang
2016-08-31 17:57:23 +03:00
Nathaniel McCallum
63b9fd0552 Add support for the cleanup attribute in GCC/Clang
The new json_auto_t macro allows easy declaration of json_t types that
automatically decrement at the end of their scope.
2016-08-31 08:57:57 -04:00
Petri Lehtinen
b45745118d doc: Fix json_error_t::position type
Fixes #300
2016-08-31 12:18:25 +03:00
Petri Lehtinen
0ffecdbade doc: Fix json_error_t::position type
Fixes #300
2016-08-31 12:17:03 +03:00
Petri Lehtinen
ab1ba69027 Set documentation version to 2.9-dev 2016-08-30 21:19:51 +03:00
Petri Lehtinen
a5610c8895 Set documentation version to 2.8 2016-08-30 21:19:13 +03:00
Petri Lehtinen
012c5f0eca jansson 2.8 2016-08-30 21:09:28 +03:00
Petri Lehtinen
a931aace16 jansson 2.8 2016-08-26 20:35:52 +03:00
Petri Lehtinen
14573dc920 Merge pull request #293 from akheron/object-insertion-order
Always preserve insertion order of object items
2016-08-25 20:20:34 +03:00
Petri Lehtinen
71594af7d5 Merge pull request #298 from pasiopou/oom-crash
OOM crash
2016-08-11 22:15:01 +03:00
Andreas Pasiopoulos
6a4b3f878d Conform to c89 2016-08-11 18:51:29 +03:00
Andreas Pasiopoulos
86fdf76f79 Check the allocation was successful before freeing existing hashtable buckets
and increasing hashtable order

Fixes a crash observed when there is OOM in hashtable_do_rehash
2016-08-11 17:48:29 +03:00
Andreas Pasiopoulos
835290dfdf Add a test case for OOM while allocating memory 2016-08-11 17:18:46 +03:00
Petri Lehtinen
7daffabf07 Fix a typo 2016-06-24 06:55:49 +03:00
Petri Lehtinen
9df267054f Always preserve insertion order of object items 2016-06-20 21:10:23 +03:00
Petri Lehtinen
8f067962f6 Merge pull request #290 from Thynix/documentation
Polish API documentation
2016-06-20 07:29:29 +03:00
Steve Dougherty
603fdc9154 doc: fix code block formatting 2016-06-17 10:45:47 -04:00
Petri Lehtinen
520340998f Merge pull request #289 from akheron/simplify-snprintf-checks
Simplify snprintf and vsnprintf checking for Visual Studio
2016-06-03 12:29:55 +03:00
Petri Lehtinen
2d710d832f appveyor.yml: Build on all Visual Studio versions 2016-06-03 12:08:36 +03:00
Steve Dougherty
a8c834c882 doc: improve conciseness
The object_to_key_iter() example is now formatted like
json_object_foreach() and json_object_foreach(). The edited descriptions were
redundant, and the removed headers didn't add useful information.
2016-06-01 11:27:22 -04:00
Steve Dougherty
7438cc8ba8 doc: correct argument names
The function signatures use "json", not "root".
2016-06-01 11:27:22 -04:00
Petri Lehtinen
72fd2fec4c Simplify snprintf and vsnprintf checking for Visual Studio 2016-05-30 11:51:39 +03:00
Petri Lehtinen
762b299e71 Fix subnormal number parsing on mingw32
Patch by Blanca Elio Rosario.
2016-05-17 11:31:55 +03:00
Petri Lehtinen
013c3892c3 Merge pull request #284 from dev-zzo/issue-282
Don't crash on deeply nested values by limiting parser recursion depth
2016-05-03 13:07:46 +03:00
Dmitry Janushkevich
64ce0ad373 Fix for issue #282
The fix limits recursion depths when parsing arrays and objects.
The limit is configurable via the `JSON_PARSER_MAX_DEPTH` setting
within `jansson_config.h` and is set by default to 2048.

Update the RFC conformance document to note the limit; the RFC
allows limits to be set by the implementation so nothing has
actually changed w.r.t. conformance state.

Reported by Gustavo Grieco.
2016-05-03 10:22:06 +02:00
Petri Lehtinen
087ed94c45 Change readthedocs domain 2016-04-28 07:04:00 +03:00
Petri Lehtinen
006638a6a2 Merge pull request #275 from Mephistophiles/master
Fix proto in doc
2016-03-03 07:26:20 +02:00
Maxim Zhukov
130c2fff31 Fix proto in doc 2016-03-02 17:40:49 +03:00
Petri Lehtinen
cfd817895a doc: Fix a typo 2016-02-25 07:29:28 +02:00
Petri Lehtinen
ac97e0bf79 json_pack(): Document s?, o?, O? 2016-02-25 07:23:53 +02:00
Petri Lehtinen
1927eeb4b4 Merge pull request #270 from Alteous/master
Implemented json_pack() format specifiers 's?', 'o?', and 'O?'.
2016-02-25 07:13:04 +02:00
David Harvey-Macaulay
c8361441fe Revised commit. 2016-02-23 19:33:52 +00:00
David Harvey-Macaulay
107cfe9499 Implemented json_pack() format specifiers 's?', 'o?', and 'O?'. 2016-02-23 19:28:56 +00:00
David Harvey-Macaulay
811965b475 Implemented json_pack() format specifiers 's?', 'o?', and 'O?'. 2016-02-22 21:32:20 +00:00
Petri Lehtinen
e08101704c Merge pull request #268 from wingyplus/fix-libdir-pkg-config
Fix libdir is wrong path when compile with cmake
2016-02-11 07:29:57 +02:00
Petri Lehtinen
c17be5870b Merge pull request #267 from Mephistophiles/master
Optimized functions
2016-02-11 07:26:00 +02:00
Thanabodee Charoenpiriyakij
9d71d006cc Fix libdir is wrong when compile with cmake
because JANSSON_INSTALL_LIB_DIR is set to /usr/local/lib after
foreach loop
2016-02-10 18:46:43 +07:00
Maxim Zhukov
b698ca13de [Optimized functions]
Optimized json_equal, json_delete, json_copy, json_deep_copy
Replaced if to switch
Removed unnecessary check for NULL.
2016-02-03 17:34:54 +03:00
Petri Lehtinen
0f50bb10b6 Fix a typo 2015-12-23 08:18:27 +02:00
Petri Lehtinen
e6b60da043 Update CHANGES 2015-12-23 08:17:18 +02:00
Petri Lehtinen
89f4c15e6d Change doc version to 2.8-dev
It's shown in the front page of readthedocs/latest.
2015-12-23 07:40:58 +02:00
Petri Lehtinen
1c2e707b34 Fix a comment 2015-12-23 07:40:02 +02:00
Petri Lehtinen
52015cf35c Merge pull request #263 from wking/display-first-unrecognized-key
pack_unpack: List first unrecognized key in strict unpacking
2015-12-23 07:38:03 +02:00
W. Trevor King
1d513d063a pack_unpack: List unrecognized keys in strict unpacking
Otherwise figuring out what's wrong with your JSON can be tricky,
especially if you're using a single fmt string to validate a large,
complicated schema.

The comma delimiting will make separating keys that contain commas
difficult.  For example:

  {"foo, bar": true, "baz": false}

will generate errors like:

  2 object item(s) left unpacked: foo, bar, baz

but that seems like a small enough corner case to not be worth much
worrying.

I wanted to find a way to handle this without have_unrecognized_keys,
but the strbuffer tooling makes it look like I shouldn't be reaching
in to do things like:

  strbuffer_t unrecognized_keys;
  unrecognized_keys.value = NULL;

and then using 'unrecognized_keys.value == NULL' in place of
have_unrecognized_keys.
2015-12-22 10:05:31 -08:00
Petri Lehtinen
581d5b899c Add versionadded to json_get_alloc_funcs doc 2015-12-22 13:05:28 +02:00
Petri Lehtinen
4f49c07781 Merge pull request #264 from npmccallum/master
Add json_get_alloc_funcs() to allow alloc function fetching
2015-12-22 13:01:36 +02:00
Nathaniel McCallum
245e532934 Add json_get_alloc_funcs() to allow alloc function fetching
This is particularly useful in modular situations where the allocation
functions are either unknown or private. For instance, in such cases,
the caller of json_dumps() has no way to free the returned buffer.
2015-12-21 11:46:32 -05:00
Petri Lehtinen
e44b2231b5 doc: Fix a broken link
Fixes #258
2015-10-20 10:21:08 +03:00
Petri Lehtinen
40bd71f064 Merge pull request #257 from denis2342/master
changed code from while to do/while
2015-10-14 08:54:37 +03:00
Denis Ahrens
067dc50efe changed code from while to do/while 2015-10-12 16:11:56 +02:00
Petri Lehtinen
4876bda857 Merge pull request #255 from gonzzor/snprintf_dump.c
Improve usage of snprintf in dump.c
2015-10-05 15:43:45 +03:00
Jonas Jonsson
e89538f685 Format %x expects unsigned int, not int
Be more explicit that a cast is happening.
2015-10-05 14:00:44 +02:00
Jonas Jonsson
f7331c7194 Use sizeof(seq) instead of magic number 13
Be more future proof by keeping the number of magic constants low.
2015-10-05 13:59:09 +02:00
Petri Lehtinen
02464862ed Merge pull request #248 from phoenix0428/patch-1
Update test_memory_funcs.c
2015-09-08 21:13:08 +03:00
Petri Lehtinen
db0213ae56 Merge pull request #252 from OlehKulykov/master
Buildable with MS Compiler version  >= 1900
2015-09-01 21:13:25 +03:00
OlehKulykov
8f2298bad8 Buildable with MS Compiler version >= 1900 2015-08-31 23:43:29 +02:00
Petri Lehtinen
4c4f692bd6 Add json_object_foreach_safe
Fixes #230.
2015-08-26 17:32:55 +03:00
Kinam Kim
875b78dc97 Update test_memory_funcs.c 2015-08-13 16:44:31 +09:00
Petri Lehtinen
fef27e6d3e Merge branch '2.7' 2015-05-19 08:54:13 +03:00
Petri Lehtinen
1dab656dee doc: Copying doesn't preserve key insertion order
Fixes #237.
2015-05-19 08:54:09 +03:00
Petri Lehtinen
69678aaa35 doc: Copying doesn't preserve key insertion order
Fixes #237.
2015-05-19 08:49:10 +03:00
Petri Lehtinen
d384acd706 Merge pull request #234 from flok99/master
Optimized dump_indent to reduce the number of fwrite calls.
2015-05-04 09:00:26 +03:00
Folkert van Heusden
5d42e1520a Optimized dump_indent to reduce the number of fwrite calls. 2015-05-01 15:18:53 +02:00
Petri Lehtinen
d8753db4ac Merge pull request #233 from haldean/master
Use snprintf and strncpy in place of sprintf and strcpy

OpenBSD linker nags about using sprintf and strcpy.
2015-04-30 10:43:39 +03:00
Haldean
95dd927857 use snprintf and strncpy in place of sprintf and strcpy
This is both good practice and nice for OpenBSD users, who will no
longer get the nag message to not use sprintf/strcpy every time they
link against jansson. It's worth noting that the existing code seems
safe to me - bounds checks were already happening before the actual
calls - and that this is for extra security.
2015-04-30 00:11:25 -07:00
Petri Lehtinen
76760011ff Don't crash when building documentation man pages
Fixes #207.
2015-03-07 09:14:06 +02:00
Petri Lehtinen
11813f4128 Fix a use after free 2015-02-07 07:36:29 +02:00
Petri Lehtinen
8b1bdcacb7 Merge pull request #221 from lioncash/test
test_object: Fix a duplicate conditional check in test_iterators
2015-02-05 09:28:43 +02:00
Lioncash
c242b46016 test_object: Fix a duplicate conditional check in test_iterators 2015-02-05 01:26:13 -05:00
Petri Lehtinen
58c188e1d5 Merge pull request #217 from JoakimSoderberg/simple_parse_cmake
Compile simple_parse example for CMake project.
2015-01-14 08:05:51 +02:00
Petri Lehtinen
ffc18128f4 Merge pull request #216 from JoakimSoderberg/infinity_warning
Disable warning for deliberate use on MSVS.
2015-01-14 08:04:37 +02:00
Joakim Soderberg
6a38d0d431 Disable warning for deliberate use on MSVS.
Disable "warning C4756: overflow in constant arithmetic" when deliberately
triggering it in a test using infinity.
2015-01-13 16:30:00 +01:00
Joakim Soderberg
d0a8ad4c06 Compile simple_parse example for CMake project. 2015-01-13 16:17:48 +01:00
Petri Lehtinen
c244b1483e examples/README.rst: Fix formatting 2014-12-30 08:56:07 +02:00
Petri Lehtinen
fc83f10c85 Add Makefile.am for examples 2014-12-30 08:50:33 +02:00
Petri Lehtinen
8d561cd94e examples/simple_parse.c: Reindent, fix warnings & style 2014-12-30 08:50:33 +02:00
Petri Lehtinen
970c6988a5 Merge pull request #214 from rdpoor/master
adding simple-parse example
2014-12-30 08:33:34 +02:00
Robert Poor
220dcb7be3 use fgets() rather than getline() 2014-12-29 06:22:19 -08:00
Robert Poor
48e0488f07 adding simple-parse example 2014-12-26 10:49:15 -08:00
Petri Lehtinen
890760b2fb Increase test coverage 2014-12-19 08:35:46 +02:00
Petri Lehtinen
15653c47dd Add a test case for \u0000 2014-12-19 07:57:33 +02:00
Petri Lehtinen
5508ab403d Honor JSON_DECODE_INT_AS_REAL at lexical stage
This has the consequence that numbers are never converted to integers
when JSON_DECODE_INT_AS_REAL is set, and thus it works correctly all
integers that are representable as double.

Fixes #212.
2014-12-18 15:16:14 +02:00
Petri Lehtinen
d799ee11b4 doc: Update the Getting Started chapter
- It's not true anymore that there are no configure options that modify
  Jansson's behaviour

- Remove trailing whitespace
2014-12-18 14:47:08 +02:00
Petri Lehtinen
abaae7630e Make it possible to set initial hashtable size
Fixes #213.
2014-12-18 14:43:44 +02:00
Petri Lehtinen
5c1d87592a wip: add tests 2014-12-15 08:30:59 +02:00
Petri Lehtinen
5885035f5f Add coveralls.io badge to README.rst 2014-12-15 08:14:57 +02:00
Petri Lehtinen
dee4a7c29e Merge pull request #211 from JoakimSoderberg/coveralls
Add support for coverage/coveralls.io in cmake project.
2014-12-15 08:09:13 +02:00
david bigagli
82a55ef205 doc patch for jansson 2014-12-15 07:47:19 +02:00
Joakim Söderberg
19f33c0e71 Add support for coverage/coveralls.io in cmake project.
This adds support for http://coveralls.io/ to the cmake project. This can then be run via a new Travis job, which uploads json containing the coverage data to the website.

To use this, please login usin github at http://coveralls.io/ and enable the Jansson project. You can then get a nice percentage badge for code coverage after each Travis buid. Coveralls will also comment on pull request with coverage info.

To test and run it locally do:

```bash
$ mkdir build && cd build
$ cmake -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug ..
$ cmake --build .                      # $ make
$ cmake --build . --target coveralls   # $ make coveralls
```

There is also another script that generates a local HTML page using lcov CodeCoverage.cmake which can be run using

```bash
$ make coverage
```

The required depdencies to run this are:
gcov
curl
lcov (is needed for the normal CodeCoverage script)
2014-12-12 23:57:01 +01:00
Petri Lehtinen
3c9e5c9925 Move the list member to the top of hashtable_pair struct
This may make debugging easier with limited debuggers.
2014-12-10 07:41:41 +02:00
Petri Lehtinen
1c38ab17f5 Include jansson_config.h with quotes
Fixes #209.
2014-12-04 10:45:26 +02:00
Petri Lehtinen
d5edfcc6fd Merge pull request #208 from skeid21/master
Fix warnings on LLVM 6.0 targetion iOS arm64.
2014-11-29 11:52:11 +02:00
Shawn Harris
9b435df3d4 Warnings - use size_t where appropriate to prevent warning when compilation is targeting 64 bit 2014-11-28 18:11:26 -07:00
Petri Lehtinen
bc743ad2d9 README: Documentation is hosted at RTD 2014-10-20 20:27:56 +03:00
Petri Lehtinen
19cc800ad3 Merge pull request #206 from chrullrich/fix-win-build
Fix CMake configuration for Windows build
2014-10-14 12:28:19 +03:00
Christian Ullrich
b52e7a69aa Fix CFLAGS for static-CRT build.
Setting the variables to only "/MT(d)" resets all other flags to their defaults, so the debug build used release flags.
2014-10-13 13:06:02 +02:00
Christian Ullrich
1395e4303a Fix static CRT selection. 2014-10-13 13:04:04 +02:00
Petri Lehtinen
d7a6269a17 Use expr instead of $((...)) in shell scripts
For Solaris 10 compatibility.
2014-10-03 08:49:08 +03:00
Petri Lehtinen
7fbe7c3960 Don't use the nonstandard __FUNCTION__ macro 2014-10-03 08:38:57 +03:00
102 changed files with 6737 additions and 3203 deletions

5
.clang-format Normal file
View File

@@ -0,0 +1,5 @@
BasedOnStyle: LLVM
AlignConsecutiveMacros: true
ColumnLimit: 90
IndentCaseLabels: true
IndentWidth: 4

31
.github/workflows/fuzz.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: oss-fuzz
on:
pull_request:
branches: [ master ]
paths:
- '**.c'
- '**.h'
jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'jansson'
dry-run: false
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'jansson'
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts

80
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: ./scripts/clang-format-check
autotools:
strategy:
matrix:
os: ["ubuntu-latest", "macos-latest"]
cc: ["gcc", "clang"]
runs-on: ${{matrix.os}}
steps:
- if: ${{runner.os == 'macOS'}}
run: brew install autoconf automake libtool
- uses: actions/checkout@v2
- run: autoreconf -fi
- env:
CC: ${{matrix.cc}}
CFLAGS: -Werror
run: ./configure
- run: make check
cmake:
strategy:
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
cc: ["gcc", "clang"]
exclude:
- os: windows-latest
cc: gcc
- os: windows-latest
cc: clang
include:
- os: windows-latest
cc: 'msvc' # Doesn't really matter, MSVC is always used on Windows
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- env:
CC: ${{matrix.cc}}
run: cmake .
- run: cmake --build .
- run: ctest
valgrind:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo apt update && sudo apt install valgrind
- run: cmake -DJANSSON_TEST_WITH_VALGRIND=ON .
- run: cmake --build .
- run: ctest
coveralls:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo apt update && sudo apt install curl lcov
- run: cmake -DJANSSON_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .
- run: cmake --build .
- run: cmake --build . --target coverage
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: coverage.info

4
.gitignore vendored
View File

@@ -31,3 +31,7 @@ stamp-h1
/jansson_private_config.h
/build
*.exe
.idea
cmake-build-debug/
*.log
*.trs

View File

@@ -1,14 +0,0 @@
env:
matrix:
- JANSSON_BUILD_METHOD=cmake JANSSON_CMAKE_OPTIONS="-DJANSSON_TEST_WITH_VALGRIND=ON" JANSSON_EXTRA_INSTALL="valgrind"
- JANSSON_BUILD_METHOD=autotools
language: c
compiler:
- gcc
- clang
install:
- sudo apt-get update -qq
- sudo apt-get install -y -qq cmake $JANSSON_EXTRA_INSTALL
script:
- if [ "$JANSSON_BUILD_METHOD" = "autotools" ]; then autoreconf -f -i && CFLAGS=-Werror ./configure && make check; fi
- if [ "$JANSSON_BUILD_METHOD" = "cmake" ]; then mkdir build && cd build && cmake .. $JANSSON_CMAKE_OPTIONS && cmake --build . && ctest --output-on-failure; fi

307
CHANGES
View File

@@ -1,3 +1,310 @@
Version 2.14
============
Released 2021-09-09
* New Features:
- Add `json_object_getn`, `json_object_setn`, `json_object_deln`, and the
corresponding `nocheck` functions. (#520, by Maxim Zhukov)
* Fixes:
- Handle `sprintf` corner cases (#537, by Tobias Stoeckmann)
* Build:
- Symbol versioning for all exported symbols (#540, by Simon McVittie)
- Fix compiler warnings (#555, by Kelvin Lee)
* Documentation:
- Small fixes (#544, #546, by @i-ky)
- Sphinx 3 compatibility (#543, by Pierce Lopez)
Version 2.13.1
==============
Released 2020-05-07
* Build:
- Include `jansson_version_str()` and `jansson_version_cmp()` in
shared library. (#534)
- Include ``scripts/`` in tarball. (#535)
Version 2.13
============
Released 2020-05-05
* New Features:
- Add `jansson_version_str()` and `jansson_version_cmp()` for runtime
version checking (#465).
- Add `json_object_update_new()`, `json_object_update_existing_new()`
and `json_object_update_missing_new()` functions (#499).
- Add `json_object_update_recursive()` (#505).
* Build:
- Add ``-Wno-format-truncation`` to suppress format truncation warnings (#489).
* Bug fixes:
- Remove ``strtod`` macro definition for MinGW (#498).
- Add infinite loop check in `json_deep_copy()` (#490).
- Add ``pipe`` macro definition for MinGW (#500).
- Enhance ``JANSSON_ATTRS`` macro to support earlier C standard(C89) (#501).
- Update version detection for sphinx-build (#502).
* Documentation:
- Fix typos (#483, #494).
- Document that call the custom free function to free the return value
of `json_dumps()` if you have a custom malloc/free (#490).
- Add vcpkg installation instructions (#496).
- Document that non-blocking file descriptor is not supported on
`json_loadfd()` (#503).
Version 2.12
============
Released 2018-11-26
* Bug fixes:
- Fix error message in `json_pack()` for NULL object (#409).
- Avoid invalid memory read in `json_pack()` (#421).
- Call va_end after va_copy in `json_vsprintf()` (#427).
- Improve handling of formats with '?' and '*' in `json_pack()` (#438).
- Remove inappropriate `jsonp_free()` which caused segmentation fault in
error handling (#444).
* Build:
- Add function attributes for GCC and CLANG to provide warnings on improper
use of jansson routines (#404).
- Many CMake fixes (#408, #412, #415).
- Enable -Bsymbolic-functions linker flag whenever possible.
- Resolve various compiler warnings (#423, #430, #435, #436).
- Fix code coverage ignored paths (#439).
* Other:
- Test coverage improvements (#398, #400).
- Add VS 2017 to appveyor, update Visual Studio documentation (#417).
- Update copyright for 2018 (#424).
- Update install instructions in README (#401).
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
============
Released 2017-03-02
* New features:
- Add JSON_EMBED encoding flag allowing arrays and objects to be encoded
into existing streams (#329).
- Add `json_dumpb()` function for dumping to a pre-allocated buffer (#328).
- Add `json_dumpfd()` and `json_loadfd()` functions for dumping to streaming
file descriptors (#328).
- Add support for parsing buffers larger than 2GB (#309).
* Build:
- Fix CMake build when LONG_LONG_INT is defined as "" (#321)
* Other:
- Internal code cleanup (#311, #314)
Version 2.9
===========
Released 2016-09-18
* New features:
- Add ``json_auto_t`` to automatically decref a value that goes out
of scope. Available only on GCC and Clang. (#301)
* Build:
- Fix CMake build (at least on Linux) by removing conflicting
jansson_config.h from the distribution (#306)
- Change CMake install target generation to be optional (#305)
* Documentation:
- Small documentation fixes.
Version 2.8
===========
Released 2016-08-30
* New features:
- Always preserve insertion order of object items.
`json_object_iter()` and friends, `json_object_foreach()` and
`json_dumps()` and friends now always work in the insertion order of
object items (#293).
- Add `json_object_foreach_safe()` macro that allows
`json_object_del()` calls during iteration (#230).
- Add `json_get_alloc_funcs()` to allow reading the allocation
functions set by `json_set_alloc_funcs()` (#262, #264).
- Add `json_pack()` format specifiers s?, o? and O? for values that
can be null (#261, #270).
* Bug fixes:
- Fix a crash when parsing inputs consisting of very deeply nested
arrays or objects (#282, #284).
- Never convert numbers to integers in the parser when
JSON_DECODE_INT_AS_REAL is set. This fixes error messages for
overflowing numbers when JSON_DECODE_INT_AS_REAL is set (#212).
- Fix a use-after-free in `json_pack()` error handling.
- Fix subnormal number parsing on mingw32.
- Handle out-of-memory situations gracefully in the hashtable
implementation (#298).
* Build:
- Fix build with CMake on all versions of Visual Studio up to 2015
(#262, #289).
- Fix pkgconfig libdir when using CMake (#268).
- Fix CMake config for static CRT builds on Windows (#206).
- Fix warnings on LLVM 6.0 targeting iOS arm64 (#208).
- Add coverlls.io support via Travis for a nice test coverage badge
(#211).
- Don't expect ``jansson_config.h`` to be in the compiler's include
path (#209).
- Add a build-time option to set initial hashtable size (#213).
- Use snprintf and strncpy in place of sprintf and strcpy to silence
linker warnings on OpenBSD (#233).
* Documentation:
- Fix various typos in documentation, and a broken link (#258).
- Add an example program in ``examples/`` (#214, #217).
- Fix building of documentation man pages (#207).
- Document the fact that copying objects doesn't preserve the
insertion order of keys (#237).
* Tests:
- Don't use the nonstandard __FUNCTION__ macro in tests.
- Use expr instead of $((...)) in shell scripts for Solaris 10
compatibility.
- Disable Visual Studio warning C4756 when triggered deliberately in
tests (#216).
- Other minor fixes (#221, #248).
* Other changes:
- List all unrecognized object keys when strict unpacking fails
(#263).
- Alter the order of the members of the hashtable_pair struct for
easier debugging.
- Minor performance improvement to `json_dump()` and friends (#234).
- Minor style fixes (#255, #257).
Version 2.7
===========

View File

@@ -1,54 +1,5 @@
# Notes:
#
# Author: Paul Harris, June 2012
# Additions: Joakim Soderberg, Febuary 2013
#
# Supports: building static/shared, release/debug/etc, can also build html docs
# and some of the tests.
# Note that its designed for out-of-tree builds, so it will not pollute your
# source tree.
#
# TODO 1: Finish implementing tests. api tests are working, but the valgrind
# variants are not flagging problems.
#
# TODO 2: There is a check_exports script that would try and incorporate.
#
# TODO 3: Consolidate version numbers, currently the version number is written
# into: * cmake (here) * autotools (the configure) * source code header files.
# Should not be written directly into header files, autotools/cmake can do
# that job.
#
# Brief intro on how to use cmake:
# > mkdir build (somewhere - we do out-of-tree builds)
# > use cmake, ccmake, or cmake-gui to configure the project. for linux, you
# can only choose one variant: release,debug,etc... and static or shared.
# >> example:
# >> cd build
# >> ccmake -i ../path_to_jansson_dir
# >> inside, configure your options. press C until there are no lines
# with * next to them.
# >> note, I like to configure the 'install' path to ../install, so I get
# self-contained clean installs I can point other projects to.
# >> press G to 'generate' the project files.
# >> make (to build the project)
# >> make install
# >> make test (to run the tests, if you enabled them)
#
# Brief description on how it works:
# There is a small heirachy of CMakeLists.txt files which define how the
# project is built.
# Header file detection etc is done, and the results are written into config.h
# and jansson_config.h, which are generated from the corresponding
# config.h.cmake and jansson_config.h.cmake template files.
# The generated header files end up in the build directory - not in
# the source directory.
# The rest is down to the usual make process.
cmake_minimum_required (VERSION 2.8)
# required for exports? cmake_minimum_required (VERSION 2.8.6)
project (jansson C)
cmake_minimum_required (VERSION 3.1)
project(jansson C)
# Options
option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF)
@@ -61,27 +12,33 @@ if (MSVC)
option(JANSSON_STATIC_CRT "Link the static CRT libraries" OFF )
endif ()
option(JANSSON_EXAMPLES "Compile example applications" ON)
if (UNIX)
option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF)
endif ()
# Set some nicer output dirs.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(JANSSON_TEMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/tmp)
# Give the debug version a different postfix for windows,
# so both the debug and release version can be built in the
# same build-tree on Windows (MSVC).
if (WIN32)
if (WIN32 AND NOT CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX "_d")
else (WIN32)
endif (WIN32)
endif()
# This is how I thought it should go
# set (JANSSON_VERSION "2.3.1")
# set (JANSSON_SOVERSION 2)
set(JANSSON_DISPLAY_VERSION "2.7")
set(JANSSON_DISPLAY_VERSION "2.14")
# This is what is required to match the same numbers as automake's
set(JANSSON_VERSION "4.7.0")
set(JANSSON_VERSION "4.14.0")
set(JANSSON_SOVERSION 4)
# for CheckFunctionKeywords
@@ -93,19 +50,28 @@ include (CheckFunctionKeywords)
include (CheckIncludeFiles)
include (CheckTypeSize)
# suppress format-truncation warning
include (CheckCCompilerFlag)
check_c_compiler_flag(-Wno-format-truncation HAS_NO_FORMAT_TRUNCATION)
if (HAS_NO_FORMAT_TRUNCATION)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-truncation")
endif()
if (MSVC)
# Turn off Microsofts "security" warnings.
add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" )
if (STATIC_CRT)
set(CMAKE_C_FLAGS_RELEASE "/MT")
set(CMAKE_C_FLAGS_DEBUG "/MTd")
if (JANSSON_STATIC_CRT)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
endif()
endif()
if (NOT WIN32 AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX))
add_definitions("-fPIC")
message("C compiler: ${CMAKE_C_COMPILER_ID}")
if (JANSSON_COVERAGE)
include(CodeCoverage)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
endif()
check_include_files (endian.h HAVE_ENDIAN_H)
@@ -115,7 +81,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)
@@ -141,9 +107,9 @@ if (HAVE_INT32_T)
set (JSON_INT32 int32_t)
elseif (HAVE___INT32)
set (JSON_INT32 __int32)
elseif (HAVE_LONG_INT AND (${LONG_INT} EQUAL 4))
elseif (HAVE_LONG_INT AND (LONG_INT EQUAL 4))
set (JSON_INT32 long)
elseif (HAVE_INT AND (${INT} EQUAL 4))
elseif (HAVE_INT AND (INT EQUAL 4))
set (JSON_INT32 int)
else ()
message (FATAL_ERROR "Could not detect a valid 32-bit integer type")
@@ -159,12 +125,12 @@ if (HAVE_UINT32_T)
set (JSON_UINT32 uint32_t)
elseif (HAVE___UINT32)
set (JSON_UINT32 __uint32)
elseif (HAVE_UNSIGNED_LONG_INT AND (${UNSIGNED_LONG_INT} EQUAL 4))
elseif (HAVE_UNSIGNED_LONG_INT AND (UNSIGNED_LONG_INT EQUAL 4))
set (JSON_UINT32 "unsigned long")
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 4))
elseif (HAVE_UNSIGNED_INT AND (UNSIGNED_INT EQUAL 4))
set (JSON_UINT32 "unsigned int")
else ()
message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type")
message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type")
endif ()
check_type_size (uint16_t UINT16_T)
@@ -173,12 +139,12 @@ if (HAVE_UINT16_T)
set (JSON_UINT16 uint16_t)
elseif (HAVE___UINT16)
set (JSON_UINT16 __uint16)
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 2))
elseif (HAVE_UNSIGNED_INT AND (UNSIGNED_INT EQUAL 2))
set (JSON_UINT16 "unsigned int")
elseif (HAVE_UNSIGNED_SHORT AND (${UNSIGNED_SHORT} EQUAL 2))
elseif (HAVE_UNSIGNED_SHORT AND (UNSIGNED_SHORT EQUAL 2))
set (JSON_UINT16 "unsigned short")
else ()
message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type")
message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type")
endif ()
check_type_size (uint8_t UINT8_T)
@@ -191,7 +157,7 @@ else ()
set (JSON_UINT8 "unsigned char")
endif ()
# Check for ssize_t and SSIZE_T existance.
# Check for ssize_t and SSIZE_T existence.
check_type_size(ssize_t SSIZE_T)
check_type_size(SSIZE_T UPPERCASE_SSIZE_T)
if(NOT HAVE_SSIZE_T)
@@ -227,7 +193,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)
@@ -280,31 +246,22 @@ else()
set (JSON_INLINE)
endif()
# Find our snprintf
check_function_exists (snprintf HAVE_SNPRINTF)
check_function_exists (_snprintf HAVE__SNPRINTF)
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)
if (HAVE_SNPRINTF)
set(JSON_SNPRINTF snprintf)
elseif (HAVE__SNPRINTF)
set(JSON_SNPRINTF _snprintf)
endif ()
if (HAVE_SYNC_BUILTINS)
set(JSON_HAVE_SYNC_BUILTINS 1)
else()
set(JSON_HAVE_SYNC_BUILTINS 0)
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)
if (HAVE_ATOMIC_BUILTINS)
set(JSON_HAVE_ATOMIC_BUILTINS 1)
else()
set(JSON_HAVE_ATOMIC_BUILTINS 0)
endif()
# Create pkg-conf file.
# (We use the same files as ./configure does, so we
# have to defined the same variables used there).
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR lib)
endif(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
set(VERSION ${JANSSON_DISPLAY_VERSION})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
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.")
# configure the public config file
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
@@ -336,7 +293,7 @@ set(JANSSON_HDR_PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/utf.h
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
set(JANSSON_HDR_PUBLIC
set(JANSSON_HDR_PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h)
@@ -345,22 +302,66 @@ source_group("Library Private Headers" FILES ${JANSSON_HDR_PRIVATE})
source_group("Library Public Headers" FILES ${JANSSON_HDR_PUBLIC})
if(JANSSON_BUILD_SHARED_LIBS)
add_library(jansson SHARED
${JANSSON_SRC}
${JANSSON_HDR_PRIVATE}
${JANSSON_HDR_PUBLIC}
add_library(jansson SHARED
${JANSSON_SRC}
${JANSSON_HDR_PRIVATE}
${JANSSON_HDR_PUBLIC}
src/jansson.def)
# check if linker support --default-symver
list(APPEND CMAKE_REQUIRED_LIBRARIES "-Wl,--default-symver")
check_c_source_compiles(
"
int main (void)
{
return 0;
}
"
DSYMVER_WORKS
)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "-Wl,--default-symver")
if (SYMVER_WORKS)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--default-symver")
else()
# some linkers may only support --version-script
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/jansson.sym" "JANSSON_${JANSSON_SOVERSION} {
global:
*;
};
")
list(APPEND CMAKE_REQUIRED_LIBRARIES "-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/jansson.sym")
check_c_source_compiles(
"
int main (void)
{
return 0;
}
"
VSCRIPT_WORKS
)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/jansson.sym")
if (VSCRIPT_WORKS)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/jansson.sym")
endif()
endif()
set_target_properties(jansson PROPERTIES
VERSION ${JANSSON_VERSION}
SOVERSION ${JANSSON_SOVERSION})
else()
add_library(jansson
add_library(jansson STATIC
${JANSSON_SRC}
${JANSSON_HDR_PRIVATE}
${JANSSON_HDR_PRIVATE}
${JANSSON_HDR_PUBLIC})
set_target_properties(jansson PROPERTIES
POSITION_INDEPENDENT_CODE true)
endif()
if (JANSSON_EXAMPLES)
add_executable(simple_parse "${CMAKE_CURRENT_SOURCE_DIR}/examples/simple_parse.c")
target_link_libraries(simple_parse jansson)
endif()
# For building Documentation (uses Sphinx)
option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
@@ -368,12 +369,12 @@ if (JANSSON_BUILD_DOCS)
find_package(Sphinx)
if (NOT SPHINX_FOUND)
message(WARNING "Sphinx not found. Cannot generate documentation!
message(WARNING "Sphinx not found. Cannot generate documentation!
Set -DJANSSON_BUILD_DOCS=OFF to get rid of this message.")
else()
if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
message(WARNING "Your Sphinx version is too old!
This project requires Sphinx v1.0 or above to produce
message(WARNING "Your Sphinx version is too old!
This project requires Sphinx v1.0 or above to produce
proper documentation (you have v${Sphinx_VERSION_STRING}).
You will get output but it will have errors.")
endif()
@@ -426,7 +427,7 @@ if (JANSSON_BUILD_DOCS)
list(APPEND DOC_TARGETS latex)
endif()
endif()
# The doc target will build all documentation targets.
add_custom_target(doc)
@@ -480,16 +481,20 @@ if (NOT JANSSON_WITHOUT_TESTS)
set(api_tests
test_array
test_chaos
test_copy
test_dump
test_dump_callback
test_equal
test_fixed_size
test_load
test_load_callback
test_loadb
test_number
test_object
test_pack
test_simple
test_sprintf
test_unpack)
# Doing arithmetic on void pointers is not allowed by Microsofts compiler
@@ -507,29 +512,31 @@ if (NOT JANSSON_WITHOUT_TESTS)
# Create executables and tests/valgrind tests for API tests.
foreach (test ${api_tests})
build_testprog(${test} ${PROJECT_SOURCE_DIR}/test/suites/api)
build_testprog(${test} ${CMAKE_CURRENT_SOURCE_DIR}/test/suites/api)
if (JANSSON_TEST_WITH_VALGRIND)
add_test(memcheck__${test}
${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test})
${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
else()
add_test(${test} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test})
add_test(${test}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
endif ()
endforeach ()
# Test harness for the suites tests.
build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin)
build_testprog(json_process ${CMAKE_CURRENT_SOURCE_DIR}/test/bin)
set(SUITE_TEST_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process)
set(SUITES encoding-flags valid invalid invalid-unicode)
foreach (SUITE ${SUITES})
file(GLOB TESTDIRS ${jansson_SOURCE_DIR}/test/suites/${SUITE}/*)
file(GLOB TESTDIRS test/suites/${SUITE}/*)
foreach (TESTDIR ${TESTDIRS})
if (IS_DIRECTORY ${TESTDIR})
get_filename_component(TNAME ${TESTDIR} NAME)
set(SUITE_TEST_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process)
if (JANSSON_TEST_WITH_VALGRIND)
add_test(memcheck__${SUITE}__${TNAME}
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR})
@@ -551,7 +558,13 @@ if (NOT JANSSON_WITHOUT_TESTS)
endforeach ()
endforeach ()
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
if (JANSSON_COVERAGE)
SETUP_TARGET_FOR_COVERAGE(coverage coverage ctest)
endif ()
# Enable using "make check" just like the autotools project.
# By default cmake creates a target "make test"
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS json_process ${api_tests})
endif ()
@@ -572,87 +585,78 @@ endif()
set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
# Make sure the paths are absolute.
foreach(p LIB BIN INCLUDE CMAKE)
set(var JANSSON_INSTALL_${p}_DIR)
if(NOT IS_ABSOLUTE "${${var}}")
set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
endif()
endforeach()
# Export targets (This is used for other CMake projects to easily find the libraries and include files).
export(TARGETS jansson
FILE "${PROJECT_BINARY_DIR}/JanssonTargets.cmake")
export(PACKAGE jansson)
# Generate the config file for the build-tree.
set(JANSSON__INCLUDE_DIRS
"${PROJECT_SOURCE_DIR}/include"
"${PROJECT_BINARY_DIR}/include")
set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories")
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
${PROJECT_BINARY_DIR}/JanssonConfig.cmake
@ONLY)
# Generate the config file for the installation tree.
file(RELATIVE_PATH
REL_INCLUDE_DIR
"${JANSSON_INSTALL_CMAKE_DIR}"
"${JANSSON_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir.
# Note the EVENT_CMAKE_DIR is defined in JanssonConfig.cmake.in,
# we escape it here so it's evaluated when it is included instead
# so that the include dirs are given relative to where the
# config file is located.
set(JANSSON__INCLUDE_DIRS
"\${JANSSON_CMAKE_DIR}/${REL_INCLUDE_DIR}")
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
@ONLY)
# Generate version info for both build-tree and install-tree.
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfigVersion.cmake.in
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
@ONLY)
# Define the public headers.
set_target_properties(jansson PROPERTIES PUBLIC_HEADER "${JANSSON_HDR_PUBLIC}")
#TODO: fix this.
# Create pkg-conf file.
# (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)
# Make sure the paths are relative.
foreach(p LIB BIN INCLUDE CMAKE)
set(var JANSSON_INSTALL_${p}_DIR)
endforeach()
# Generate the config file for the build-tree.
set(JANSSON__INCLUDE_DIRS
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}/include")
set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/janssonConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/janssonConfig.cmake
@ONLY)
# Generate the config file for the installation tree.
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfigVersion.cmake"
VERSION ${JANSSON_VERSION}
COMPATIBILITY ExactVersion
)
configure_package_config_file(
"cmake/janssonConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfig.cmake"
INSTALL_DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}"
)
#
# Install targets.
#
install(TARGETS jansson
EXPORT JanssonTargets
LIBRARY DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
ARCHIVE DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
RUNTIME DESTINATION "${JANSSON_INSTALL_BIN_DIR}" COMPONENT lib # Windows DLLs
PUBLIC_HEADER DESTINATION "${JANSSON_INSTALL_INCLUDE_DIR}" COMPONENT dev)
option(JANSSON_INSTALL "Generate installation target" ON)
if (JANSSON_INSTALL)
install(TARGETS jansson
EXPORT janssonTargets
LIBRARY DESTINATION "lib"
ARCHIVE DESTINATION "lib"
RUNTIME DESTINATION "bin"
INCLUDES DESTINATION "include")
# Install the pkg-config.
install (FILES
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
DESTINATION ${JANSSON_INSTALL_LIB_DIR}/pkgconfig COMPONENT dev)
install(FILES ${JANSSON_HDR_PUBLIC}
DESTINATION "include")
# Install the configs.
install(FILES
${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
# Install the pkg-config.
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
DESTINATION lib/pkgconfig)
# Install exports for the install-tree.
install(EXPORT JanssonTargets
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
# Install the configs.
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfigVersion.cmake
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}")
# Install exports for the install-tree.
install(EXPORT janssonTargets
NAMESPACE jansson::
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}")
endif()
# For use when simply using add_library from a parent project to build jansson.
set(JANSSON_LIBRARIES jansson CACHE STRING "Jansson libraries")
set(JANSSON_LIBRARIES jansson CACHE STRING "jansson libraries")

3
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,3 @@
Hi, and thanks for contributing!
Please remember to add tests and documentation for new functionality. Backwards incompatible changes or features that are not directly related to JSON are likely to be rejected.

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
Copyright (c) 2009-2020 Petri Lehtinen <petri@digip.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,4 +1,4 @@
EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android
EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android examples scripts
SUBDIRS = doc src test
# "make distcheck" builds the dvi target, so use it to check that the
@@ -8,3 +8,5 @@ dvi:
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = jansson.pc
TESTS = scripts/clang-format-check

View File

@@ -1,18 +1,21 @@
Jansson README
==============
.. image:: https://travis-ci.org/akheron/jansson.png
:target: https://travis-ci.org/akheron/jansson
.. image:: https://github.com/akheron/jansson/workflows/tests/badge.svg
:target: https://github.com/akheron/jansson/actions
.. image:: https://ci.appveyor.com/api/projects/status/lmhkkc4q8cwc65ko
:target: https://ci.appveyor.com/project/akheron/jansson
.. image:: https://coveralls.io/repos/akheron/jansson/badge.png?branch=master
:target: https://coveralls.io/r/akheron/jansson?branch=master
Jansson_ is a C library for encoding, decoding and manipulating JSON
data. Its main features and design principles are:
- Simple and intuitive API and data model
- Comprehensive documentation
- `Comprehensive documentation`_
- No dependencies on other libraries
@@ -23,12 +26,11 @@ data. Its main features and design principles are:
Jansson is licensed under the `MIT license`_; see LICENSE in the
source distribution for details.
Compilation and Installation
----------------------------
If you obtained a source tarball, just use the standard autotools
commands::
If you obtained a ``jansson-X.Y.tar.*`` tarball from GitHub Releases, just use
the standard autotools commands::
$ ./configure
$ make
@@ -38,9 +40,8 @@ To run the test suite, invoke::
$ make check
If the source has been checked out from a Git repository, the
./configure script has to be generated first. The easiest way is to
use autoreconf::
If the source has been checked out from a Git repository, the ``configure``
script has to be generated first. The easiest way is to use autoreconf::
$ autoreconf -i
@@ -48,8 +49,7 @@ use autoreconf::
Documentation
-------------
Prebuilt HTML documentation is available at
http://www.digip.org/jansson/doc/.
Documentation is available at http://jansson.readthedocs.io/en/latest/.
The documentation source is in the ``doc/`` subdirectory. To generate
HTML documentation, invoke::
@@ -60,6 +60,15 @@ Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
1.0 or newer is required to generate the documentation.
Community
---------
* `Documentation <http://jansson.readthedocs.io/en/latest/>`_
* `Issue tracker <https://github.com/akheron/jansson/issues>`_
* `Mailing list <http://groups.google.com/group/jansson-users>`_
* `Wiki <https://github.com/akheron/jansson/wiki>`_ contains some development documentation
.. _Jansson: http://www.digip.org/jansson/
.. _`Comprehensive documentation`: http://jansson.readthedocs.io/en/latest/
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
.. _Sphinx: http://sphinx.pocoo.org/

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -36,4 +36,8 @@
otherwise to 0. */
#define JSON_HAVE_LOCALECONV 0
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
#endif

View File

@@ -1,6 +1,18 @@
environment:
matrix:
- VS: Visual Studio 9 2008
- VS: Visual Studio 10 2010
- VS: Visual Studio 11 2012
- VS: Visual Studio 12 2013
- VS: Visual Studio 14 2015
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VS: Visual Studio 15 2017
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
VS: Visual Studio 16 2019
build_script:
- md build
- cd build
- cmake ..
- cmake -G "%VS%" ..
- cmake --build . --config Release
- ctest --output-on-failure
- ctest --output-on-failure

163
cmake/CodeCoverage.cmake Normal file
View File

@@ -0,0 +1,163 @@
#
# Boost Software License - Version 1.0 - August 17th, 2003
#
# Permission is hereby granted, free of charge, to any person or organization
# obtaining a copy of the software and accompanying documentation covered by
# this license (the "Software") to use, reproduce, display, distribute,
# execute, and transmit the Software, and to prepare derivative works of the
# Software, and to permit third-parties to whom the Software is furnished to
# do so, all subject to the following:
#
# The copyright notices in the Software and this entire statement, including
# the above license grant, this restriction and the following disclaimer,
# must be included in all copies of the Software, in whole or in part, and
# all derivative works of the Software, unless such copies or derivative
# works are solely in the form of machine-executable object code generated by
# a source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# USAGE:
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF(NOT (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC))
# Clang version 3.0.0 and greater now supports gcov as well.
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
IF(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...")
ENDIF()
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
IF ( NOT CMAKE_BUILD_TYPE STREQUAL "Debug" )
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
# Param _targetname The name of new the custom make target
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
ENDIF() # NOT GENHTML_PATH
# Setup target
ADD_CUSTOM_TARGET(${_targetname}
# Cleanup lcov
${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info --rc lcov_branch_coverage=1
COMMAND ${LCOV_PATH} --remove ${_outputname}.info '*/build/include/*' '*/test/*' '/usr/include/*' --output-file ${_outputname}.info --rc lcov_branch_coverage=1
# COMMAND ${GENHTML_PATH} --branch-coverage -o ${_outputname} ${_outputname}.info.cleaned
# COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests
# Param _outputname cobertura output is generated as _outputname.xml
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
IF(NOT PYTHON_EXECUTABLE)
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
ENDIF() # NOT PYTHON_EXECUTABLE
IF(NOT GCOVR_PATH)
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
ENDIF() # NOT GCOVR_PATH
ADD_CUSTOM_TARGET(${_targetname}
# Run tests
${_testrunner} ${ARGV3}
# Running gcovr
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA

View File

@@ -241,27 +241,42 @@ endif ()
# ----------------------------------------------------------------------------
# determine Sphinx version
# some quick experiments by @ploxiln
# - sphinx 1.7 and later have the version output format like "sphinx-build 1.7.2"
# - sphinx 1.2 through 1.6 have the version output format like "Sphinx (sphinx-build) 1.2.2"
# - sphinx 1.1 and before do not have a "--version" flag, but it causes the help output like "-h" does which includes version like "Sphinx v1.0.2"
if (Sphinx-build_EXECUTABLE)
# intentionally use invalid -h option here as the help that is shown then
# will include the Sphinx version information
if (Sphinx_PYTHON_EXECUTABLE)
execute_process (
COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" -h
COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" --version
OUTPUT_VARIABLE _Sphinx_VERSION
ERROR_VARIABLE _Sphinx_VERSION
)
elseif (UNIX)
execute_process (
COMMAND "${Sphinx-build_EXECUTABLE}" -h
COMMAND "${Sphinx-build_EXECUTABLE}" --version
OUTPUT_VARIABLE _Sphinx_VERSION
ERROR_VARIABLE _Sphinx_VERSION
)
endif ()
# The sphinx version can also contain a "b" instead of the last dot.
# For example "Sphinx v1.2b1" so we cannot just split on "."
if (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+(\\.|b)[0-9]+)")
# For example "Sphinx v1.2b1" or "Sphinx 1.7.0b2" so we cannot just split on "."
if (_Sphinx_VERSION MATCHES "sphinx-build ([0-9]+\\.[0-9]+(\\.|a?|b?)([0-9]*)(b?)([0-9]*))")
set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}")
set (_SPHINX_VERSION_FOUND)
elseif (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+(\\.|b?)([0-9]*)(b?)([0-9]*))")
set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}")
set (_SPHINX_VERSION_FOUND)
elseif (_Sphinx_VERSION MATCHES "Sphinx \\(sphinx-build\\) ([0-9]+\\.[0-9]+(\\.|a?|b?)([0-9]*)(b?)([0-9]*))")
set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}")
set (_SPHINX_VERSION_FOUND)
endif ()
endif ()
if(_SPHINX_VERSION_FOUND)
string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MAJOR ${Sphinx_VERSION_STRING})
string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MINOR ${Sphinx_VERSION_STRING})
string(REGEX REPLACE "[0-9]+\\.[0-9]+(\\.|b)([0-9]+)" "\\1" Sphinx_VERSION_PATCH ${Sphinx_VERSION_STRING})
@@ -270,7 +285,6 @@ if (Sphinx-build_EXECUTABLE)
if (Sphinx_VERSION_PATCH EQUAL 0)
string (REGEX REPLACE "\\.0$" "" Sphinx_VERSION_STRING "${Sphinx_VERSION_STRING}")
endif ()
endif()
endif ()
# ----------------------------------------------------------------------------

View File

@@ -1,17 +0,0 @@
# - Config file for the jansson package
# It defines the following variables
# JANSSON_INCLUDE_DIRS - include directories for FooBar
# JANSSON_LIBRARIES - libraries to link against
# Get the path of the current file.
get_filename_component(JANSSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# Set the include directories.
set(JANSSON_INCLUDE_DIRS "@JANSSON__INCLUDE_DIRS@")
# Include the project Targets file, this contains definitions for IMPORTED targets.
include(${JANSSON_CMAKE_DIR}/JanssonTargets.cmake)
# IMPORTED targets from JanssonTargets.cmake
set(JANSSON_LIBRARIES jansson)

View File

@@ -1,11 +0,0 @@
set(PACKAGE_VERSION "@JANSSON_DISPLAY_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/janssonTargets.cmake")
check_required_components("@PROJECT_NAME@")

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -59,6 +59,16 @@
/* If locale.h and localeconv() are available, define to 1, 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
#endif

View File

@@ -47,13 +47,7 @@
# define ssize_t @JSON_SSIZE@
#endif
#cmakedefine HAVE_SNPRINTF 1
#ifndef HAVE_SNPRINTF
# define snprintf @JSON_SNPRINTF@
#endif
#cmakedefine HAVE_VSNPRINTF
#cmakedefine USE_URANDOM 1
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@

View File

@@ -1,5 +1,5 @@
AC_PREREQ([2.60])
AC_INIT([jansson], [2.7], [petri@digip.org])
AC_INIT([jansson], [2.14], [https://github.com/akheron/jansson/issues])
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([1.10 foreign])
@@ -9,6 +9,7 @@ AC_CONFIG_HEADERS([jansson_private_config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
AC_PROG_LIBTOOL
AM_CONDITIONAL([GCC], [test x$GCC = xyes])
@@ -38,25 +39,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
@@ -92,8 +101,68 @@ AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1],
[Define to 1 if CryptGenRandom should be used for seeding the hash function])
fi
AC_ARG_ENABLE([initial-hashtable-order],
[AS_HELP_STRING([--enable-initial-hashtable-order=VAL],
[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.])],
[initial_hashtable_order=$enableval], [initial_hashtable_order=3])
AC_DEFINE_UNQUOTED([INITIAL_HASHTABLE_ORDER], [$initial_hashtable_order],
[Number of buckets new object hashtables contain is 2 raised to this power. E.g. 3 -> 2^3 = 8.])
AC_ARG_ENABLE([Bsymbolic],
[AS_HELP_STRING([--disable-Bsymbolic],
[Avoid linking with -Bsymbolic-function])],
[], [with_Bsymbolic=check])
if test "x$with_Bsymbolic" != "xno" ; then
AC_MSG_CHECKING([for -Bsymbolic-functions linker flag])
saved_LDFLAGS="${LDFLAGS}"
LDFLAGS=-Wl,-Bsymbolic-functions
AC_TRY_LINK(
[], [int main (void) { return 0; }],
[AC_MSG_RESULT([yes])
have_Bsymbolic=yes],
[AC_MSG_RESULT([no])
have_Bsymbolic=no]
)
LDFLAGS="${saved_LDFLAGS}"
if test "x$with_Bsymbolic" = "xcheck" ; then
with_Bsymbolic=$have_Bsymbolic;
fi
if test "x$with_Bsymbolic:x$have_Bsymbolic" = "xyes:xno" ; then
AC_MSG_ERROR([linker support is required for -Bsymbolic])
fi
fi
AS_IF([test "x$with_Bsymbolic" = "xyes"], [JSON_BSYMBOLIC_LDFLAGS=-Wl[,]-Bsymbolic-functions])
AC_SUBST(JSON_BSYMBOLIC_LDFLAGS)
# Enable symbol versioning on GNU libc
JSON_SYMVER_LDFLAGS=
AC_CHECK_DECL([__GLIBC__], [JSON_SYMVER_LDFLAGS=-Wl,--default-symver])
AC_SUBST([JSON_SYMVER_LDFLAGS])
AC_ARG_ENABLE([ossfuzzers],
[AS_HELP_STRING([--enable-ossfuzzers],
[Whether to generate the fuzzers for OSS-Fuzz])],
[have_ossfuzzers=yes], [have_ossfuzzers=no])
AM_CONDITIONAL([USE_OSSFUZZERS], [test "x$have_ossfuzzers" = "xyes"])
AC_SUBST([LIB_FUZZING_ENGINE])
AM_CONDITIONAL([USE_OSSFUZZ_FLAG], [test "x$LIB_FUZZING_ENGINE" = "x-fsanitize=fuzzer"])
AM_CONDITIONAL([USE_OSSFUZZ_STATIC], [test -f "$LIB_FUZZING_ENGINE"])
if test x$GCC = xyes; then
AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement"
AC_MSG_CHECKING(for -Wno-format-truncation)
wnoformat_truncation="-Wno-format-truncation"
AS_IF([${CC} -Wno-format-truncation -Werror -S -o /dev/null -xc /dev/null > /dev/null 2>&1],
[AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)
wnoformat_truncation=""])
AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement -Wshadow ${wnoformat_truncation}"
fi
AC_SUBST([AM_CFLAGS])
@@ -105,6 +174,7 @@ AC_CONFIG_FILES([
src/jansson_config.h
test/Makefile
test/bin/Makefile
test/ossfuzz/Makefile
test/suites/Makefile
test/suites/api/Makefile
])

View File

@@ -1,5 +1,5 @@
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst portability.rst \
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst threadsafety.rst \
tutorial.rst upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build

File diff suppressed because it is too large Load Diff

View File

@@ -41,14 +41,14 @@ master_doc = 'index'
# General information about the project.
project = u'Jansson'
copyright = u'2009-2014, Petri Lehtinen'
copyright = u'2009-2020, Petri Lehtinen'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '2.7'
version = '2.14'
# The full version, including alpha/beta/rc tags.
release = version

View File

@@ -22,8 +22,7 @@ JSON strings are mapped to C-style null-terminated character arrays,
and UTF-8 encoding is used internally.
All Unicode codepoints U+0000 through U+10FFFF are allowed in string
values. However, U+0000 is not allowed in object keys because of API
restrictions.
values. However, U+0000 is allowed in object keys only for length-aware functions.
Unicode normalization or any other transformation is never performed
on any strings (string values or object keys). When checking for
@@ -108,3 +107,13 @@ types, ``long double``, etc. Obviously, shorter types like ``short``,
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.
Depth of nested values
======================
To avoid stack exhaustion, Jansson currently limits the nesting depth
for arrays and objects to a certain value (default: 2048), defined as
a macro ``JSON_PARSER_MAX_DEPTH`` within ``jansson_config.h``.
The limit is allowed to be set by the RFC; there is no recommended value
or required minimum depth to be supported.

View File

@@ -19,13 +19,13 @@
<description of the json_object function>
:copyright: Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
:copyright: Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
:license: MIT, see LICENSE for details.
"""
from docutils import nodes
from docutils.parsers.rst import Directive
class refcounting(nodes.emphasis): pass
def visit(self, node):
self.visit_emphasis(node)
@@ -40,20 +40,30 @@ def html_depart(self, node):
self.body.append('</em>')
def refcounting_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
if arguments[0] == 'borrow':
text = 'Return value: Borrowed reference.'
elif arguments[0] == 'new':
text = 'Return value: New reference.'
else:
raise Error('Valid arguments: new, borrow')
class refcounting(nodes.emphasis):
pass
class refcounting_directive(Directive):
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
def run(self):
if self.arguments[0] == 'borrow':
text = 'Return value: Borrowed reference.'
elif self.arguments[0] == 'new':
text = 'Return value: New reference.'
else:
raise Error('Valid arguments: new, borrow')
return [refcounting(text, text)]
return [refcounting(text, text)]
def setup(app):
app.add_node(refcounting,
html=(html_visit, html_depart),
latex=(visit, depart),
text=(visit, depart))
app.add_directive('refcounting', refcounting_directive, 0, (1, 0, 0))
text=(visit, depart),
man=(visit, depart))
app.add_directive('refcounting', refcounting_directive)

View File

@@ -30,8 +30,7 @@ compiling and installing is extremely simple::
To change the destination directory (``/usr/local`` by default), use
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
--help`` for the list of all possible installation options. (There are
no options to customize the resulting Jansson binary.)
--help`` for the list of all possible configuration options.
The command ``make check`` runs the test suite distributed with
Jansson. This step is not strictly necessary, but it may find possible
@@ -44,7 +43,7 @@ version control. To create the script, the build system needs to be
bootstrapped. There are many ways to do this, but the easiest one is
to use ``autoreconf``::
autoreconf -vi
autoreconf -fi
This command creates the ``./configure`` script, which can then be
used as described above.
@@ -83,10 +82,15 @@ Generating make files on unix:
mkdir build
cd build
cmake .. # or `ccmake ..` for a GUI.
cmake .. # or ccmake .. for a GUI.
.. note::
If you don't want to build docs or ``Sphinx`` is not installed, you should add ``"-DJANSSON_BUILD_DOCS=OFF"`` in the ``cmake`` command.
Then to build::
make
make check
make install
@@ -102,12 +106,28 @@ Creating Visual Studio project files from the command line:
md build
cd build
cmake -G "Visual Studio 10" ..
cmake -G "Visual Studio 15 2017" ..
.. note::
You should replace the name of the generator (``-G`` flag) matching
the Visual Studio version installed on your system. Currently, the
following versions are supported:
- ``Visual Studio 9 2008``
- ``Visual Studio 10 2010``
- ``Visual Studio 11 2012``
- ``Visual Studio 12 2013``
- ``Visual Studio 14 2015``
- ``Visual Studio 15 2017``
- ``Visual Studio 16 2019``
Any later version should also work.
You will now have a *Visual Studio Solution* in your build directory.
To run the unit tests build the ``RUN_TESTS`` project.
If you prefer a GUI the ``cmake`` line in the above example can
If you prefer a GUI the ``cmake`` line in the above example can
be replaced with::
cmake-gui ..
@@ -117,15 +137,30 @@ for CMake_ simply run::
cmake
To list available CMake_ settings (and what they are currently set to)
To list available CMake_ settings (and what they are currently set to)
for the project, run::
cmake -LH ..
Windows (MinGW)
^^^^^^^^^^^^^^^
If you prefer using MinGW on Windows, make sure MinGW installed and ``{MinGW}/bin`` has been added to ``PATH``, then do the following commands:
.. parsed-literal::
<unpack>
cd jansson-|release|
md build
cd build
cmake -G "MinGW Makefiles" ..
mingw32-make
Mac OSX (Xcode)
^^^^^^^^^^^^^^^
If you prefer using Xcode instead of make files on OSX,
do the following. (Use the same steps as
do the following. (Use the same steps as
for :ref:`Unix <build-cmake-unix>`)::
...
@@ -145,7 +180,7 @@ static library. To build the shared version use::
Changing install directory (same as autoconf --prefix)
""""""""""""""""""""""""""""""""""""""""""""""""""""""
Just as with the autoconf_ project you can change the destination directory
for ``make install``. The equivalent for autoconfs ``./configure --prefix``
for ``make install``. The equivalent for autoconfs ``./configure --prefix``
in CMake_ is::
...
@@ -220,7 +255,9 @@ link the program as follows::
cc -o prog prog.c -ljansson
Starting from version 1.2, there's also support for pkg-config_::
Starting from version 1.2, there's also support for pkg-config_:
.. code-block:: shell
cc -o prog prog.c `pkg-config --cflags --libs jansson`

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,37 +8,33 @@
#include <stdlib.h>
#include <string.h>
#include <jansson.h>
#include <curl/curl.h>
#include <jansson.h>
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
#define URL_SIZE 256
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
#define URL_SIZE 256
/* Return the offset of the first newline in text or the length of
text if there's no newline */
static int newline_offset(const char *text)
{
static int newline_offset(const char *text) {
const char *newline = strchr(text, '\n');
if(!newline)
if (!newline)
return strlen(text);
else
return (int)(newline - text);
}
struct write_result
{
struct write_result {
char *data;
int pos;
};
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
{
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) {
struct write_result *result = (struct write_result *)stream;
if(result->pos + size * nmemb >= BUFFER_SIZE - 1)
{
if (result->pos + size * nmemb >= BUFFER_SIZE - 1) {
fprintf(stderr, "error: too small buffer\n");
return 0;
}
@@ -49,8 +45,7 @@ static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
return size * nmemb;
}
static char *request(const char *url)
{
static char *request(const char *url) {
CURL *curl = NULL;
CURLcode status;
struct curl_slist *headers = NULL;
@@ -59,17 +54,14 @@ static char *request(const char *url)
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(!curl)
if (!curl)
goto error;
data = malloc(BUFFER_SIZE);
if(!data)
if (!data)
goto error;
struct write_result write_result = {
.data = data,
.pos = 0
};
struct write_result write_result = {.data = data, .pos = 0};
curl_easy_setopt(curl, CURLOPT_URL, url);
@@ -81,16 +73,14 @@ static char *request(const char *url)
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
status = curl_easy_perform(curl);
if(status != 0)
{
if (status != 0) {
fprintf(stderr, "error: unable to request data from %s:\n", url);
fprintf(stderr, "%s\n", curl_easy_strerror(status));
goto error;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if(code != 200)
{
if (code != 200) {
fprintf(stderr, "error: server responded with code %ld\n", code);
goto error;
}
@@ -105,18 +95,17 @@ static char *request(const char *url)
return data;
error:
if(data)
if (data)
free(data);
if(curl)
if (curl)
curl_easy_cleanup(curl);
if(headers)
if (headers)
curl_slist_free_all(headers);
curl_global_cleanup();
return NULL;
}
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
size_t i;
char *text;
char url[URL_SIZE];
@@ -124,8 +113,7 @@ int main(int argc, char *argv[])
json_t *root;
json_error_t error;
if(argc != 3)
{
if (argc != 3) {
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
return 2;
@@ -134,65 +122,57 @@ int main(int argc, char *argv[])
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
text = request(url);
if(!text)
if (!text)
return 1;
root = json_loads(text, 0, &error);
free(text);
if(!root)
{
if (!root) {
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
return 1;
}
if(!json_is_array(root))
{
if (!json_is_array(root)) {
fprintf(stderr, "error: root is not an array\n");
json_decref(root);
return 1;
}
for(i = 0; i < json_array_size(root); i++)
{
for (i = 0; i < json_array_size(root); i++) {
json_t *data, *sha, *commit, *message;
const char *message_text;
data = json_array_get(root, i);
if(!json_is_object(data))
{
if (!json_is_object(data)) {
fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1));
json_decref(root);
return 1;
}
sha = json_object_get(data, "sha");
if(!json_is_string(sha))
{
if (!json_is_string(sha)) {
fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1));
json_decref(root);
return 1;
}
commit = json_object_get(data, "commit");
if(!json_is_object(commit))
{
if (!json_is_object(commit)) {
fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1));
json_decref(root);
return 1;
}
message = json_object_get(commit, "message");
if(!json_is_string(message))
{
if (!json_is_string(message)) {
fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1));
json_decref(root);
return 1;
}
message_text = json_string_value(message);
printf("%.8s %.*s\n",
json_string_value(sha),
newline_offset(message_text),
printf("%.8s %.*s\n", json_string_value(sha), newline_offset(message_text),
message_text);
}

View File

@@ -41,7 +41,7 @@ Contents
upgrading
tutorial
conformance
portability
threadsafety
apiref
changes

View File

@@ -1,35 +1,34 @@
***********
Portability
***********
.. _portability-thread-safety:
.. _thread-safety:
*************
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
@@ -62,7 +61,7 @@ program startup. See :ref:`apiref-custom-memory-allocation`.
Locale
------
======
Jansson works fine under any locale.

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
@@ -74,7 +74,7 @@ function::
static char *request(const char *url);
It takes the URL as a parameter, preforms a HTTP GET request, and
It takes the URL as a parameter, performs a HTTP GET request, and
returns a newly allocated string that contains the response body. If
the request fails, an error message is printed to stderr and the
return value is *NULL*. For full details, refer to :download:`the code
@@ -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);
}
@@ -256,7 +256,9 @@ For a detailed explanation of reference counting in Jansson, see
:ref:`apiref-reference-count` in :ref:`apiref`.
The program's ready, let's test it and view the latest commits in
Jansson's repository::
Jansson's repository:
.. code-block:: shell
$ ./github_commits akheron jansson
1581f26a Merge branch '2.3'

View File

@@ -47,13 +47,13 @@ List of Incompatible Changes
**Underlying type of JSON integers**
The underlying C type of JSON integers has been changed from
:type:`int` to the widest available signed integer type, i.e.
:type:`long long` or :type:`long`, depending on whether
:type:`long long` is supported on your system or not. This makes
``int`` to the widest available signed integer type, i.e.
``long long`` or ``long``, depending on whether
``long long`` is supported on your system or not. This makes
the whole 64-bit integer range available on most modern systems.
``jansson.h`` has a typedef :type:`json_int_t` to the underlying
integer type. :type:`int` should still be used in most cases when
integer type. ``int`` should still be used in most cases when
dealing with smallish JSON integers, as the compiler handles
implicit type coercion. Only when the full 64-bit range is needed,
:type:`json_int_t` should be explicitly used.
@@ -69,8 +69,8 @@ List of Incompatible Changes
**Unsigned integers in API functions**
Version 2.0 unifies unsigned integer usage in the API. All uses of
:type:`unsigned int` and :type:`unsigned long` have been replaced
with :type:`size_t`. This includes flags, container sizes, etc.
``unsigned int`` and ``unsigned long`` have been replaced
with ``size_t``. This includes flags, container sizes, etc.
This should not require source code changes, as both
:type:`unsigned int` and :type:`unsigned long` are usually
compatible with :type:`size_t`.
``unsigned int`` and ``unsigned long`` are usually
compatible with ``size_t``.

4
examples/README.rst Normal file
View File

@@ -0,0 +1,4 @@
Jansson examples
================
This directory contains simple example programs that use Jansson.

200
examples/simple_parse.c Normal file
View File

@@ -0,0 +1,200 @@
/*
* Simple example of parsing and printing JSON using jansson.
*
* SYNOPSIS:
* $ examples/simple_parse
* Type some JSON > [true, false, null, 1, 0.0, -0.0, "", {"name": "barney"}]
* JSON Array of 8 elements:
* JSON True
* JSON False
* JSON Null
* JSON Integer: "1"
* JSON Real: 0.000000
* JSON Real: -0.000000
* JSON String: ""
* JSON Object of 1 pair:
* JSON Key: "name"
* JSON String: "barney"
*
* Copyright (c) 2014 Robert Poor <rdpoor@gmail.com>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <stdio.h>
#include <stdlib.h>
/* forward refs */
void print_json(json_t *root);
void print_json_aux(json_t *element, int indent);
void print_json_indent(int indent);
const char *json_plural(size_t count);
void print_json_object(json_t *element, int indent);
void print_json_array(json_t *element, int indent);
void print_json_string(json_t *element, int indent);
void print_json_integer(json_t *element, int indent);
void print_json_real(json_t *element, int indent);
void print_json_true(json_t *element, int indent);
void print_json_false(json_t *element, int indent);
void print_json_null(json_t *element, int indent);
void print_json(json_t *root) { print_json_aux(root, 0); }
void print_json_aux(json_t *element, int indent) {
switch (json_typeof(element)) {
case JSON_OBJECT:
print_json_object(element, indent);
break;
case JSON_ARRAY:
print_json_array(element, indent);
break;
case JSON_STRING:
print_json_string(element, indent);
break;
case JSON_INTEGER:
print_json_integer(element, indent);
break;
case JSON_REAL:
print_json_real(element, indent);
break;
case JSON_TRUE:
print_json_true(element, indent);
break;
case JSON_FALSE:
print_json_false(element, indent);
break;
case JSON_NULL:
print_json_null(element, indent);
break;
default:
fprintf(stderr, "unrecognized JSON type %d\n", json_typeof(element));
}
}
void print_json_indent(int indent) {
int i;
for (i = 0; i < indent; i++) {
putchar(' ');
}
}
const char *json_plural(size_t count) { return count == 1 ? "" : "s"; }
void print_json_object(json_t *element, int indent) {
size_t size;
const char *key;
json_t *value;
print_json_indent(indent);
size = json_object_size(element);
printf("JSON Object of %lld pair%s:\n", (long long)size, json_plural(size));
json_object_foreach(element, key, value) {
print_json_indent(indent + 2);
printf("JSON Key: \"%s\"\n", key);
print_json_aux(value, indent + 2);
}
}
void print_json_array(json_t *element, int indent) {
size_t i;
size_t size = json_array_size(element);
print_json_indent(indent);
printf("JSON Array of %lld element%s:\n", (long long)size, json_plural(size));
for (i = 0; i < size; i++) {
print_json_aux(json_array_get(element, i), indent + 2);
}
}
void print_json_string(json_t *element, int indent) {
print_json_indent(indent);
printf("JSON String: \"%s\"\n", json_string_value(element));
}
void print_json_integer(json_t *element, int indent) {
print_json_indent(indent);
printf("JSON Integer: \"%" JSON_INTEGER_FORMAT "\"\n", json_integer_value(element));
}
void print_json_real(json_t *element, int indent) {
print_json_indent(indent);
printf("JSON Real: %f\n", json_real_value(element));
}
void print_json_true(json_t *element, int indent) {
(void)element;
print_json_indent(indent);
printf("JSON True\n");
}
void print_json_false(json_t *element, int indent) {
(void)element;
print_json_indent(indent);
printf("JSON False\n");
}
void print_json_null(json_t *element, int indent) {
(void)element;
print_json_indent(indent);
printf("JSON Null\n");
}
/*
* Parse text into a JSON object. If text is valid JSON, returns a
* json_t structure, otherwise prints and error and returns null.
*/
json_t *load_json(const char *text) {
json_t *root;
json_error_t error;
root = json_loads(text, 0, &error);
if (root) {
return root;
} else {
fprintf(stderr, "json error on line %d: %s\n", error.line, error.text);
return (json_t *)0;
}
}
/*
* Print a prompt and return (by reference) a null-terminated line of
* text. Returns NULL on eof or some error.
*/
char *read_line(char *line, int max_chars) {
printf("Type some JSON > ");
fflush(stdout);
return fgets(line, max_chars, stdin);
}
/* ================================================================
* main
*/
#define MAX_CHARS 4096
int main(int argc, char *argv[]) {
char line[MAX_CHARS];
if (argc != 1) {
fprintf(stderr, "Usage: %s\n", argv[0]);
exit(-1);
}
while (read_line(line, MAX_CHARS) != (char *)NULL) {
/* parse text into JSON structure */
json_t *root = load_json(line);
if (root) {
/* print and release the JSON structure */
print_json(root);
json_decref(root);
}
}
return 0;
}

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

3
scripts/clang-format Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
find . -type f -a '(' -name '*.c' -o -name '*.h' ')' | xargs clang-format -i

27
scripts/clang-format-check Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
CLANG_FORMAT=${CLANG_FORMAT:-clang-format}
CLANG_FORMAT_VERSION=${CLANG_FORMAT_VERSION:-}
if ! type $CLANG_FORMAT >/dev/null || \
! $CLANG_FORMAT --version | grep -q "version ${CLANG_FORMAT_VERSION}"; then
# If running tests, mark this test as skipped.
exit 77
fi
errors=0
paths=$(git ls-files | grep '\.[ch]$')
for path in $paths; do
in=$(cat $path)
out=$($CLANG_FORMAT $path)
if [ "$in" != "$out" ]; then
diff -u -L $path -L "$path.formatted" $path - <<<$out
errors=1
fi
done
if [ $errors -ne 0 ]; then
echo "Formatting errors detected, run ./scripts/clang-format to fix!"
exit 1
fi

View File

@@ -1,6 +1,7 @@
EXTRA_DIST = jansson.def
include_HEADERS = jansson.h jansson_config.h
include_HEADERS = jansson.h
nodist_include_HEADERS = jansson_config.h
lib_LTLIBRARIES = libjansson.la
libjansson_la_SOURCES = \
@@ -19,8 +20,11 @@ libjansson_la_SOURCES = \
strconv.c \
utf.c \
utf.h \
value.c
value.c \
version.c
libjansson_la_LDFLAGS = \
-no-undefined \
-export-symbols-regex '^json_' \
-version-info 11:0:7
-export-symbols-regex '^json_|^jansson_' \
-version-info 18:0:14 \
@JSON_SYMVER_LDFLAGS@ \
@JSON_BSYMBOLIC_LDFLAGS@

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -9,141 +9,175 @@
#define _GNU_SOURCE
#endif
#include "jansson_private.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "jansson.h"
#include "jansson_private.h"
#include "strbuffer.h"
#include "utf.h"
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
#define FLAGS_TO_INDENT(f) ((f)&0x1F)
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
struct object_key {
size_t serial;
const char *key;
struct buffer {
const size_t size;
size_t used;
char *data;
};
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
{
static int dump_to_strbuffer(const char *buffer, size_t size, void *data) {
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
}
static int dump_to_file(const char *buffer, size_t size, void *data)
{
static int dump_to_buffer(const char *buffer, size_t size, void *data) {
struct buffer *buf = (struct buffer *)data;
if (buf->used + size <= buf->size)
memcpy(&buf->data[buf->used], buffer, size);
buf->used += size;
return 0;
}
static int dump_to_file(const char *buffer, size_t size, void *data) {
FILE *dest = (FILE *)data;
if(fwrite(buffer, size, 1, dest) != 1)
if (fwrite(buffer, size, 1, dest) != 1)
return -1;
return 0;
}
static int dump_to_fd(const char *buffer, size_t size, void *data) {
#ifdef HAVE_UNISTD_H
int *dest = (int *)data;
if (write(*dest, buffer, size) == (ssize_t)size)
return 0;
#endif
return -1;
}
/* 32 spaces (the maximum indentation size) */
static const char whitespace[] = " ";
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
{
if(FLAGS_TO_INDENT(flags) > 0)
{
int i, ws_count = FLAGS_TO_INDENT(flags);
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump,
void *data) {
if (FLAGS_TO_INDENT(flags) > 0) {
unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
if(dump("\n", 1, data))
if (dump("\n", 1, data))
return -1;
for(i = 0; i < depth; i++)
{
if(dump(whitespace, ws_count, data))
while (n_spaces > 0) {
int cur_n =
n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
if (dump(whitespace, cur_n, data))
return -1;
n_spaces -= cur_n;
}
}
else if(space && !(flags & JSON_COMPACT))
{
} else if (space && !(flags & JSON_COMPACT)) {
return dump(" ", 1, data);
}
return 0;
}
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
{
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data,
size_t flags) {
const char *pos, *end, *lim;
int32_t codepoint;
int32_t codepoint = 0;
if(dump("\"", 1, data))
if (dump("\"", 1, data))
return -1;
end = pos = str;
lim = str + len;
while(1)
{
while (1) {
const char *text;
char seq[13];
int length;
while(end < lim)
{
while (end < lim) {
end = utf8_iterate(pos, lim - pos, &codepoint);
if(!end)
if (!end)
return -1;
/* mandatory escape or control char */
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
if (codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
break;
/* slash */
if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
if ((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
break;
/* non-ASCII */
if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
if ((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
break;
pos = end;
}
if(pos != str) {
if(dump(str, pos - str, data))
if (pos != str) {
if (dump(str, pos - str, data))
return -1;
}
if(end == pos)
if (end == pos)
break;
/* handle \, /, ", and control codes */
length = 2;
switch(codepoint)
{
case '\\': text = "\\\\"; break;
case '\"': text = "\\\""; break;
case '\b': text = "\\b"; break;
case '\f': text = "\\f"; break;
case '\n': text = "\\n"; break;
case '\r': text = "\\r"; break;
case '\t': text = "\\t"; break;
case '/': text = "\\/"; break;
default:
{
switch (codepoint) {
case '\\':
text = "\\\\";
break;
case '\"':
text = "\\\"";
break;
case '\b':
text = "\\b";
break;
case '\f':
text = "\\f";
break;
case '\n':
text = "\\n";
break;
case '\r':
text = "\\r";
break;
case '\t':
text = "\\t";
break;
case '/':
text = "\\/";
break;
default: {
/* codepoint is in BMP */
if(codepoint < 0x10000)
{
sprintf(seq, "\\u%04X", codepoint);
if (codepoint < 0x10000) {
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
length = 6;
}
/* not in BMP -> construct a UTF-16 surrogate pair */
else
{
else {
int32_t first, last;
codepoint -= 0x10000;
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
last = 0xDC00 | (codepoint & 0x003ff);
sprintf(seq, "\\u%04X\\u%04X", first, last);
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first,
(unsigned int)last);
length = 12;
}
@@ -152,7 +186,7 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
}
}
if(dump(text, length, data))
if (dump(text, length, data))
return -1;
str = pos = end;
@@ -161,27 +195,33 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
return dump("\"", 1, data);
}
static int object_key_compare_keys(const void *key1, const void *key2)
{
return strcmp(((const struct object_key *)key1)->key,
((const struct object_key *)key2)->key);
struct key_len {
const char *key;
int len;
};
static int compare_keys(const void *key1, const void *key2) {
const struct key_len *k1 = key1;
const struct key_len *k2 = key2;
const size_t min_size = k1->len < k2->len ? k1->len : k2->len;
int res = memcmp(k1->key, k2->key, min_size);
if (res)
return res;
return k1->len - k2->len;
}
static int object_key_compare_serials(const void *key1, const void *key2)
{
size_t a = ((const struct object_key *)key1)->serial;
size_t b = ((const struct object_key *)key2)->serial;
static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents,
json_dump_callback_t dump, void *data) {
int embed = flags & JSON_EMBED;
return a < b ? -1 : a == b ? 0 : 1;
}
flags &= ~JSON_EMBED;
static int do_dump(const json_t *json, size_t flags, int depth,
json_dump_callback_t dump, void *data)
{
if(!json)
if (!json)
return -1;
switch(json_typeof(json)) {
switch (json_typeof(json)) {
case JSON_NULL:
return dump("null", 4, data);
@@ -191,222 +231,190 @@ static int do_dump(const json_t *json, size_t flags, int depth,
case JSON_FALSE:
return dump("false", 5, data);
case JSON_INTEGER:
{
case JSON_INTEGER: {
char buffer[MAX_INTEGER_STR_LENGTH];
int size;
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
"%" JSON_INTEGER_FORMAT,
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT,
json_integer_value(json));
if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
if (size < 0 || size >= MAX_INTEGER_STR_LENGTH)
return -1;
return dump(buffer, size, data);
}
case JSON_REAL:
{
case JSON_REAL: {
char buffer[MAX_REAL_STR_LENGTH];
int size;
double value = json_real_value(json);
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
FLAGS_TO_PRECISION(flags));
if(size < 0)
if (size < 0)
return -1;
return dump(buffer, size, data);
}
case JSON_STRING:
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
return dump_string(json_string_value(json), json_string_length(json), dump,
data, flags);
case JSON_ARRAY:
{
int i;
int n;
json_array_t *array;
case JSON_ARRAY: {
size_t n;
size_t i;
/* Space for "0x", double the sizeof a pointer for the hex and a
* terminator. */
char key[2 + (sizeof(json) * 2) + 1];
size_t key_len;
/* detect circular references */
array = json_to_array(json);
if(array->visited)
goto array_error;
array->visited = 1;
if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len))
return -1;
n = json_array_size(json);
if(dump("[", 1, data))
goto array_error;
if(n == 0) {
array->visited = 0;
return dump("]", 1, data);
if (!embed && dump("[", 1, data))
return -1;
if (n == 0) {
hashtable_del(parents, key, key_len);
return embed ? 0 : dump("]", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
goto array_error;
if (dump_indent(flags, depth + 1, 0, dump, data))
return -1;
for(i = 0; i < n; ++i) {
if(do_dump(json_array_get(json, i), flags, depth + 1,
dump, data))
goto array_error;
for (i = 0; i < n; ++i) {
if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump,
data))
return -1;
if(i < n - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
goto array_error;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
goto array_error;
if (i < n - 1) {
if (dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
return -1;
} else {
if (dump_indent(flags, depth, 0, dump, data))
return -1;
}
}
array->visited = 0;
return dump("]", 1, data);
array_error:
array->visited = 0;
return -1;
hashtable_del(parents, key, key_len);
return embed ? 0 : dump("]", 1, data);
}
case JSON_OBJECT:
{
json_object_t *object;
case JSON_OBJECT: {
void *iter;
const char *separator;
int separator_length;
char loop_key[LOOP_KEY_LEN];
size_t loop_key_len;
if(flags & JSON_COMPACT) {
if (flags & JSON_COMPACT) {
separator = ":";
separator_length = 1;
}
else {
} else {
separator = ": ";
separator_length = 2;
}
/* detect circular references */
object = json_to_object(json);
if(object->visited)
goto object_error;
object->visited = 1;
if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key),
&loop_key_len))
return -1;
iter = json_object_iter((json_t *)json);
if(dump("{", 1, data))
goto object_error;
if(!iter) {
object->visited = 0;
return dump("}", 1, data);
if (!embed && dump("{", 1, data))
return -1;
if (!iter) {
hashtable_del(parents, loop_key, loop_key_len);
return embed ? 0 : dump("}", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
goto object_error;
if (dump_indent(flags, depth + 1, 0, dump, data))
return -1;
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
{
struct object_key *keys;
if (flags & JSON_SORT_KEYS) {
struct key_len *keys;
size_t size, i;
int (*cmp_func)(const void *, const void *);
size = json_object_size(json);
keys = jsonp_malloc(size * sizeof(struct object_key));
if(!keys)
goto object_error;
keys = jsonp_malloc(size * sizeof(struct key_len));
if (!keys)
return -1;
i = 0;
while(iter)
{
keys[i].serial = hashtable_iter_serial(iter);
keys[i].key = json_object_iter_key(iter);
while (iter) {
struct key_len *keylen = &keys[i];
keylen->key = json_object_iter_key(iter);
keylen->len = json_object_iter_key_len(iter);
iter = json_object_iter_next((json_t *)json, iter);
i++;
}
assert(i == size);
if(flags & JSON_SORT_KEYS)
cmp_func = object_key_compare_keys;
else
cmp_func = object_key_compare_serials;
qsort(keys, size, sizeof(struct key_len), compare_keys);
qsort(keys, size, sizeof(struct object_key), cmp_func);
for(i = 0; i < size; i++)
{
const char *key;
for (i = 0; i < size; i++) {
const struct key_len *key;
json_t *value;
key = keys[i].key;
value = json_object_get(json, key);
key = &keys[i];
value = json_object_getn(json, key->key, key->len);
assert(value);
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, dump, data))
{
dump_string(key->key, key->len, dump, data, flags);
if (dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, parents, dump, data)) {
jsonp_free(keys);
goto object_error;
return -1;
}
if(i < size - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
{
if (i < size - 1) {
if (dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data)) {
jsonp_free(keys);
goto object_error;
return -1;
}
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
{
} else {
if (dump_indent(flags, depth, 0, dump, data)) {
jsonp_free(keys);
goto object_error;
return -1;
}
}
}
jsonp_free(keys);
}
else
{
} else {
/* Don't sort keys */
while(iter)
{
while (iter) {
void *next = json_object_iter_next((json_t *)json, iter);
const char *key = json_object_iter_key(iter);
const size_t key_len = json_object_iter_key_len(iter);
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;
dump_string(key, key_len, dump, data, flags);
if (dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1, parents,
dump, data))
return -1;
if(next)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
goto object_error;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
goto object_error;
if (next) {
if (dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
return -1;
} else {
if (dump_indent(flags, depth, 0, dump, data))
return -1;
}
iter = next;
}
}
object->visited = 0;
return dump("}", 1, data);
object_error:
object->visited = 0;
return -1;
hashtable_del(parents, loop_key, loop_key_len);
return embed ? 0 : dump("}", 1, data);
}
default:
@@ -415,15 +423,14 @@ static int do_dump(const json_t *json, size_t flags, int depth,
}
}
char *json_dumps(const json_t *json, size_t flags)
{
char *json_dumps(const json_t *json, size_t flags) {
strbuffer_t strbuff;
char *result;
if(strbuffer_init(&strbuff))
if (strbuffer_init(&strbuff))
return NULL;
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
if (json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
result = NULL;
else
result = jsonp_strdup(strbuffer_value(&strbuff));
@@ -432,31 +439,52 @@ char *json_dumps(const json_t *json, size_t flags)
return result;
}
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) {
struct buffer buf = {size, 0, buffer};
if (json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
return 0;
return buf.used;
}
int json_dumpf(const json_t *json, FILE *output, size_t flags) {
return json_dump_callback(json, dump_to_file, (void *)output, flags);
}
int json_dump_file(const json_t *json, const char *path, size_t flags)
{
int json_dumpfd(const json_t *json, int output, size_t flags) {
return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
}
int json_dump_file(const json_t *json, const char *path, size_t flags) {
int result;
FILE *output = fopen(path, "w");
if(!output)
if (!output)
return -1;
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)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
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

@@ -1,63 +1,59 @@
#include <string.h>
#include "jansson_private.h"
#include <string.h>
void jsonp_error_init(json_error_t *error, const char *source)
{
if(error)
{
void jsonp_error_init(json_error_t *error, const char *source) {
if (error) {
error->text[0] = '\0';
error->line = -1;
error->column = -1;
error->position = 0;
if(source)
if (source)
jsonp_error_set_source(error, source);
else
error->source[0] = '\0';
}
}
void jsonp_error_set_source(json_error_t *error, const char *source)
{
void jsonp_error_set_source(json_error_t *error, const char *source) {
size_t length;
if(!error || !source)
if (!error || !source)
return;
length = strlen(source);
if(length < JSON_ERROR_SOURCE_LENGTH)
strcpy(error->source, source);
if (length < JSON_ERROR_SOURCE_LENGTH)
strncpy(error->source, source, length + 1);
else {
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
strcpy(error->source, "...");
strcpy(error->source + 3, source + extra);
memcpy(error->source, "...", 3);
strncpy(error->source + 3, source + extra, length - extra + 1);
}
}
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, const char *msg, ...)
{
void jsonp_error_set(json_error_t *error, int line, int column, 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)
{
if(!error)
void jsonp_error_vset(json_error_t *error, int line, int column, size_t position,
enum json_error_code code, const char *msg, va_list ap) {
if (!error)
return;
if(error->text[0] != '\0') {
if (error->text[0] != '\0') {
/* error already set */
return;
}
error->line = line;
error->column = column;
error->position = position;
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

@@ -1,24 +1,28 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#if HAVE_CONFIG_H
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdlib.h>
#include <string.h>
#if HAVE_STDINT_H
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <jansson_config.h> /* for JSON_INLINE */
#include "jansson_private.h" /* for container_of() */
#include "hashtable.h"
#include "jansson_private.h" /* for container_of() */
#include <jansson_config.h> /* for JSON_INLINE */
#ifndef INITIAL_HASHTABLE_ORDER
#define INITIAL_HASHTABLE_ORDER 3
#endif
typedef struct hashtable_list list_t;
typedef struct hashtable_pair pair_t;
@@ -29,66 +33,57 @@ extern volatile uint32_t hashtable_seed;
/* Implementation of the hash function */
#include "lookup3.h"
#define list_to_pair(list_) container_of(list_, pair_t, list)
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
#define list_to_pair(list_) container_of(list_, pair_t, list)
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
#define hash_str(key, len) ((size_t)hashlittle((key), len, hashtable_seed))
static JSON_INLINE void list_init(list_t *list)
{
static JSON_INLINE void list_init(list_t *list) {
list->next = list;
list->prev = list;
}
static JSON_INLINE void list_insert(list_t *list, list_t *node)
{
static JSON_INLINE void list_insert(list_t *list, list_t *node) {
node->next = list;
node->prev = list->prev;
list->prev->next = node;
list->prev = node;
}
static JSON_INLINE void list_remove(list_t *list)
{
static JSON_INLINE void list_remove(list_t *list) {
list->prev->next = list->next;
list->next->prev = list->prev;
}
static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
{
static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) {
return bucket->first == &hashtable->list && bucket->first == bucket->last;
}
static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
list_t *list)
{
if(bucket_is_empty(hashtable, bucket))
{
static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, list_t *list) {
if (bucket_is_empty(hashtable, bucket)) {
list_insert(&hashtable->list, list);
bucket->first = bucket->last = list;
}
else
{
} else {
list_insert(bucket->first, list);
bucket->first = list;
}
}
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
const char *key, size_t hash)
{
const char *key, size_t key_len, size_t hash) {
list_t *list;
pair_t *pair;
if(bucket_is_empty(hashtable, bucket))
if (bucket_is_empty(hashtable, bucket))
return NULL;
list = bucket->first;
while(1)
{
while (1) {
pair = list_to_pair(list);
if(pair->hash == hash && strcmp(pair->key, key) == 0)
if (pair->hash == hash && pair->key_len == key_len &&
memcmp(pair->key, key, key_len) == 0)
return pair;
if(list == bucket->last)
if (list == bucket->last)
break;
list = list->next;
@@ -98,9 +93,8 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
}
/* returns 0 on success, -1 if key was not found */
static int hashtable_do_del(hashtable_t *hashtable,
const char *key, size_t hash)
{
static int hashtable_do_del(hashtable_t *hashtable, const char *key, size_t key_len,
size_t hash) {
pair_t *pair;
bucket_t *bucket;
size_t index;
@@ -108,20 +102,21 @@ static int hashtable_do_del(hashtable_t *hashtable,
index = hash & hashmask(hashtable->order);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
if (!pair)
return -1;
if(&pair->list == bucket->first && &pair->list == bucket->last)
if (&pair->list == bucket->first && &pair->list == bucket->last)
bucket->first = bucket->last = &hashtable->list;
else if(&pair->list == bucket->first)
else if (&pair->list == bucket->first)
bucket->first = pair->list.next;
else if(&pair->list == bucket->last)
else if (&pair->list == bucket->last)
bucket->last = pair->list.prev;
list_remove(&pair->list);
list_remove(&pair->ordered_list);
json_decref(pair->value);
jsonp_free(pair);
@@ -130,13 +125,11 @@ static int hashtable_do_del(hashtable_t *hashtable,
return 0;
}
static void hashtable_do_clear(hashtable_t *hashtable)
{
static void hashtable_do_clear(hashtable_t *hashtable) {
list_t *list, *next;
pair_t *pair;
for(list = hashtable->list.next; list != &hashtable->list; list = next)
{
for (list = hashtable->list.next; list != &hashtable->list; list = next) {
next = list->next;
pair = list_to_pair(list);
json_decref(pair->value);
@@ -144,31 +137,31 @@ static void hashtable_do_clear(hashtable_t *hashtable)
}
}
static int hashtable_do_rehash(hashtable_t *hashtable)
{
static int hashtable_do_rehash(hashtable_t *hashtable) {
list_t *list, *next;
pair_t *pair;
size_t i, index, new_size;
size_t i, index, new_size, new_order;
struct hashtable_bucket *new_buckets;
jsonp_free(hashtable->buckets);
new_order = hashtable->order + 1;
new_size = hashsize(new_order);
hashtable->order++;
new_size = hashsize(hashtable->order);
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if(!hashtable->buckets)
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if (!new_buckets)
return -1;
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
jsonp_free(hashtable->buckets);
hashtable->buckets = new_buckets;
hashtable->order = new_order;
for (i = 0; i < hashsize(hashtable->order); i++) {
hashtable->buckets[i].first = hashtable->buckets[i].last = &hashtable->list;
}
list = hashtable->list.next;
list_init(&hashtable->list);
for(; list != &hashtable->list; list = next) {
for (; list != &hashtable->list; list = next) {
next = list->next;
pair = list_to_pair(list);
index = pair->hash % new_size;
@@ -178,174 +171,169 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
return 0;
}
int hashtable_init(hashtable_t *hashtable)
{
int hashtable_init(hashtable_t *hashtable) {
size_t i;
hashtable->size = 0;
hashtable->order = 3;
hashtable->order = INITIAL_HASHTABLE_ORDER;
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
if(!hashtable->buckets)
if (!hashtable->buckets)
return -1;
list_init(&hashtable->list);
list_init(&hashtable->ordered_list);
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
for (i = 0; i < hashsize(hashtable->order); i++) {
hashtable->buckets[i].first = hashtable->buckets[i].last = &hashtable->list;
}
return 0;
}
void hashtable_close(hashtable_t *hashtable)
{
void hashtable_close(hashtable_t *hashtable) {
hashtable_do_clear(hashtable);
jsonp_free(hashtable->buckets);
}
int hashtable_set(hashtable_t *hashtable,
const char *key, size_t serial,
json_t *value)
{
static pair_t *init_pair(json_t *value, const char *key, size_t key_len, size_t hash) {
pair_t *pair;
/* offsetof(...) returns the size of pair_t without the last,
flexible member. This way, the correct amount is
allocated. */
if (key_len >= (size_t)-1 - offsetof(pair_t, key)) {
/* Avoid an overflow if the key is very long */
return NULL;
}
pair = jsonp_malloc(offsetof(pair_t, key) + key_len + 1);
if (!pair)
return NULL;
pair->hash = hash;
memcpy(pair->key, key, key_len);
pair->key[key_len] = '\0';
pair->key_len = key_len;
pair->value = value;
list_init(&pair->list);
list_init(&pair->ordered_list);
return pair;
}
int hashtable_set(hashtable_t *hashtable, const char *key, size_t key_len,
json_t *value) {
pair_t *pair;
bucket_t *bucket;
size_t hash, index;
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= hashsize(hashtable->order))
if(hashtable_do_rehash(hashtable))
if (hashtable->size >= hashsize(hashtable->order))
if (hashtable_do_rehash(hashtable))
return -1;
hash = hash_str(key);
hash = hash_str(key, key_len);
index = hash & hashmask(hashtable->order);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
if(pair)
{
if (pair) {
json_decref(pair->value);
pair->value = value;
}
else
{
/* offsetof(...) returns the size of pair_t without the last,
flexible member. This way, the correct amount is
allocated. */
} else {
pair = init_pair(value, key, key_len, hash);
size_t len = strlen(key);
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
/* Avoid an overflow if the key is very long */
if (!pair)
return -1;
}
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
if(!pair)
return -1;
pair->hash = hash;
pair->serial = serial;
strcpy(pair->key, key);
pair->value = value;
list_init(&pair->list);
insert_to_bucket(hashtable, bucket, &pair->list);
list_insert(&hashtable->ordered_list, &pair->ordered_list);
hashtable->size++;
}
return 0;
}
void *hashtable_get(hashtable_t *hashtable, const char *key)
{
void *hashtable_get(hashtable_t *hashtable, const char *key, size_t key_len) {
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hash_str(key);
hash = hash_str(key, key_len);
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
if (!pair)
return NULL;
return pair->value;
}
int hashtable_del(hashtable_t *hashtable, const char *key)
{
size_t hash = hash_str(key);
return hashtable_do_del(hashtable, key, hash);
int hashtable_del(hashtable_t *hashtable, const char *key, size_t key_len) {
size_t hash = hash_str(key, key_len);
return hashtable_do_del(hashtable, key, key_len, hash);
}
void hashtable_clear(hashtable_t *hashtable)
{
void hashtable_clear(hashtable_t *hashtable) {
size_t i;
hashtable_do_clear(hashtable);
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
for (i = 0; i < hashsize(hashtable->order); i++) {
hashtable->buckets[i].first = hashtable->buckets[i].last = &hashtable->list;
}
list_init(&hashtable->list);
list_init(&hashtable->ordered_list);
hashtable->size = 0;
}
void *hashtable_iter(hashtable_t *hashtable)
{
return hashtable_iter_next(hashtable, &hashtable->list);
void *hashtable_iter(hashtable_t *hashtable) {
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
}
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
{
void *hashtable_iter_at(hashtable_t *hashtable, const char *key, size_t key_len) {
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hash_str(key);
hash = hash_str(key, key_len);
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
if (!pair)
return NULL;
return &pair->list;
return &pair->ordered_list;
}
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
{
void *hashtable_iter_next(hashtable_t *hashtable, void *iter) {
list_t *list = (list_t *)iter;
if(list->next == &hashtable->list)
if (list->next == &hashtable->ordered_list)
return NULL;
return list->next;
}
void *hashtable_iter_key(void *iter)
{
pair_t *pair = list_to_pair((list_t *)iter);
void *hashtable_iter_key(void *iter) {
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->key;
}
size_t hashtable_iter_serial(void *iter)
{
pair_t *pair = list_to_pair((list_t *)iter);
return pair->serial;
size_t hashtable_iter_key_len(void *iter) {
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->key_len;
}
void *hashtable_iter_value(void *iter)
{
pair_t *pair = list_to_pair((list_t *)iter);
void *hashtable_iter_value(void *iter) {
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->value;
}
void hashtable_iter_set(void *iter, json_t *value)
{
pair_t *pair = list_to_pair((list_t *)iter);
void hashtable_iter_set(void *iter, json_t *value) {
pair_t *pair = ordered_list_to_pair((list_t *)iter);
json_decref(pair->value);
pair->value = value;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,6 +8,9 @@
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include "jansson.h"
#include <stdlib.h>
struct hashtable_list {
struct hashtable_list *prev;
struct hashtable_list *next;
@@ -17,10 +20,11 @@ struct hashtable_list {
key-value pair. In this case, it just encodes some extra data,
too */
struct hashtable_pair {
size_t hash;
struct hashtable_list list;
struct hashtable_list ordered_list;
size_t hash;
json_t *value;
size_t serial;
size_t key_len;
char key[1];
};
@@ -32,14 +36,13 @@ struct hashtable_bucket {
typedef struct hashtable {
size_t size;
struct hashtable_bucket *buckets;
size_t order; /* hashtable has pow(2, order) buckets */
size_t order; /* hashtable has pow(2, order) buckets */
struct hashtable_list list;
struct hashtable_list ordered_list;
} hashtable_t;
#define hashtable_key_to_iter(key_) \
(&(container_of(key_, struct hashtable_pair, key)->list))
#define hashtable_key_to_iter(key_) \
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
/**
* hashtable_init - Initialize a hashtable object
@@ -51,7 +54,7 @@ typedef struct hashtable {
*
* Returns 0 on success, -1 on error (out of memory).
*/
int hashtable_init(hashtable_t *hashtable);
int hashtable_init(hashtable_t *hashtable) JANSSON_ATTRS((warn_unused_result));
/**
* hashtable_close - Release all resources used by a hashtable object
@@ -67,6 +70,7 @@ void hashtable_close(hashtable_t *hashtable);
*
* @hashtable: The hashtable object
* @key: The key
* @key: The length of key
* @serial: For addition order of keys
* @value: The value
*
@@ -77,29 +81,29 @@ void hashtable_close(hashtable_t *hashtable);
*
* Returns 0 on success, -1 on failure (out of memory).
*/
int hashtable_set(hashtable_t *hashtable,
const char *key, size_t serial,
json_t *value);
int hashtable_set(hashtable_t *hashtable, const char *key, size_t key_len, json_t *value);
/**
* hashtable_get - Get a value associated with a key
*
* @hashtable: The hashtable object
* @key: The key
* @key: The length of key
*
* Returns value if it is found, or NULL otherwise.
*/
void *hashtable_get(hashtable_t *hashtable, const char *key);
void *hashtable_get(hashtable_t *hashtable, const char *key, size_t key_len);
/**
* hashtable_del - Remove a value from the hashtable
*
* @hashtable: The hashtable object
* @key: The key
* @key: The length of key
*
* Returns 0 on success, or -1 if the key was not found.
*/
int hashtable_del(hashtable_t *hashtable, const char *key);
int hashtable_del(hashtable_t *hashtable, const char *key, size_t key_len);
/**
* hashtable_clear - Clear hashtable
@@ -132,11 +136,12 @@ void *hashtable_iter(hashtable_t *hashtable);
*
* @hashtable: The hashtable object
* @key: The key that the iterator should point to
* @key: The length of key
*
* Like hashtable_iter() but returns an iterator pointing to a
* specific key.
*/
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
void *hashtable_iter_at(hashtable_t *hashtable, const char *key, size_t key_len);
/**
* hashtable_iter_next - Advance an iterator
@@ -157,11 +162,11 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
void *hashtable_iter_key(void *iter);
/**
* hashtable_iter_serial - Retrieve the serial number pointed to by an iterator
* hashtable_iter_key_len - Retrieve the key length pointed by an iterator
*
* @iter: The iterator
*/
size_t hashtable_iter_serial(void *iter);
size_t hashtable_iter_key_len(void *iter);
/**
* hashtable_iter_value - Retrieve the value pointed by an iterator

View File

@@ -44,7 +44,6 @@
#include "jansson.h"
static uint32_t buf_to_uint32(char *data) {
size_t i;
uint32_t result = 0;
@@ -55,8 +54,6 @@ static uint32_t buf_to_uint32(char *data) {
return result;
}
/* /dev/urandom */
#if !defined(_WIN32) && defined(USE_URANDOM)
static int seed_from_urandom(uint32_t *seed) {
@@ -97,12 +94,13 @@ static int seed_from_urandom(uint32_t *seed) {
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
#include <wincrypt.h>
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags);
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
typedef BOOL(WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer,
LPCSTR pszProvider, DWORD dwProvType,
DWORD dwFlags);
typedef BOOL(WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
typedef BOOL(WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
static int seed_from_windows_cryptoapi(uint32_t *seed)
{
static int seed_from_windows_cryptoapi(uint32_t *seed) {
HINSTANCE hAdvAPI32 = NULL;
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
CRYPTGENRANDOM pCryptGenRandom = NULL;
@@ -112,10 +110,11 @@ static int seed_from_windows_cryptoapi(uint32_t *seed)
int ok;
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
if(hAdvAPI32 == NULL)
if (hAdvAPI32 == NULL)
return 1;
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
pCryptAcquireContext =
(CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
if (!pCryptAcquireContext)
return 1;
@@ -123,11 +122,13 @@ static int seed_from_windows_cryptoapi(uint32_t *seed)
if (!pCryptGenRandom)
return 1;
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
pCryptReleaseContext =
(CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
if (!pCryptReleaseContext)
return 1;
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
return 1;
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
@@ -164,16 +165,16 @@ static int seed_from_timestamp_and_pid(uint32_t *seed) {
}
static uint32_t generate_seed() {
uint32_t seed;
uint32_t seed = 0;
int done = 0;
#if !defined(_WIN32) && defined(USE_URANDOM)
if (!done && seed_from_urandom(&seed) == 0)
if (seed_from_urandom(&seed) == 0)
done = 1;
#endif
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
if (!done && seed_from_windows_cryptoapi(&seed) == 0)
if (seed_from_windows_cryptoapi(&seed) == 0)
done = 1;
#endif
@@ -190,7 +191,6 @@ static uint32_t generate_seed() {
return seed;
}
volatile uint32_t hashtable_seed = 0;
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
@@ -212,7 +212,7 @@ void json_object_seed(size_t seed) {
#ifdef HAVE_SCHED_YIELD
sched_yield();
#endif
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
} while (__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
}
}
}
@@ -239,7 +239,7 @@ void json_object_seed(size_t seed) {
sched_yield();
#endif
}
} while(hashtable_seed == 0);
} while (hashtable_seed == 0);
}
}
#elif defined(_WIN32)

View File

@@ -3,6 +3,8 @@ EXPORTS
json_true
json_false
json_null
json_sprintf
json_vsprintf
json_string
json_stringn
json_string_nocheck
@@ -32,28 +34,37 @@ EXPORTS
json_object
json_object_size
json_object_get
json_object_getn
json_object_set_new
json_object_setn_new
json_object_set_new_nocheck
json_object_setn_new_nocheck
json_object_del
json_object_deln
json_object_clear
json_object_update
json_object_update_existing
json_object_update_missing
json_object_update_recursive
json_object_iter
json_object_iter_at
json_object_iter_next
json_object_iter_key
json_object_iter_key_len
json_object_iter_value
json_object_iter_set_new
json_object_key_to_iter
json_object_seed
json_dumps
json_dumpb
json_dumpf
json_dumpfd
json_dump_file
json_dump_callback
json_loads
json_loadb
json_loadf
json_loadfd
json_load_file
json_load_callback
json_equal
@@ -66,4 +77,7 @@ EXPORTS
json_unpack_ex
json_vunpack_ex
json_set_alloc_funcs
json_get_alloc_funcs
jansson_version_str
jansson_version_cmp

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,11 +8,11 @@
#ifndef JANSSON_H
#define JANSSON_H
#include <stdio.h>
#include <stdlib.h> /* for size_t */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h> /* for size_t */
#include <jansson_config.h>
#include "jansson_config.h"
#ifdef __cplusplus
extern "C" {
@@ -20,19 +20,30 @@ extern "C" {
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 7
#define JANSSON_MICRO_VERSION 0
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 14
#define JANSSON_MICRO_VERSION 0
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.7"
#define JANSSON_VERSION "2.14"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \
(JANSSON_MINOR_VERSION << 8) | \
(JANSSON_MICRO_VERSION << 0))
#define JANSSON_VERSION_HEX \
((JANSSON_MAJOR_VERSION << 16) | (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
#if defined(__GNUC__) || defined(__clang__)
#define JANSSON_ATTRS(x) __attribute__(x)
#else
#define JANSSON_ATTRS(x)
#endif
/* types */
@@ -49,7 +60,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 */
@@ -66,18 +77,18 @@ typedef long json_int_t;
#endif /* JSON_INTEGER_IS_LONG_LONG */
#endif
#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_real(json) ((json) && json_typeof(json) == JSON_REAL)
#define json_is_number(json) (json_is_integer(json) || json_is_real(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
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
#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_real(json) ((json) && json_typeof(json) == JSON_REAL)
#define json_is_number(json) (json_is_integer(json) || json_is_real(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
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
/* construction, destruction, reference counting */
@@ -91,34 +102,54 @@ json_t *json_integer(json_int_t value);
json_t *json_real(double value);
json_t *json_true(void);
json_t *json_false(void);
#define json_boolean(val) ((val) ? json_true() : json_false())
#define json_boolean(val) ((val) ? json_true() : json_false())
json_t *json_null(void);
static JSON_INLINE
json_t *json_incref(json_t *json)
{
if(json && json->refcount != (size_t)-1)
++json->refcount;
/* 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_INTERNAL_INCREF(json);
return json;
}
/* do not call json_delete directly */
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)
static JSON_INLINE void json_decref(json_t *json) {
if (json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0)
json_delete(json);
}
#if defined(__GNUC__) || defined(__clang__)
static JSON_INLINE void json_decrefp(json_t **json) {
if (json) {
json_decref(*json);
*json = NULL;
}
}
#define json_auto_t json_t __attribute__((cleanup(json_decrefp)))
#endif
/* error reporting */
#define JSON_ERROR_TEXT_LENGTH 160
#define JSON_ERROR_SOURCE_LENGTH 80
#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;
@@ -126,57 +157,138 @@ 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 */
void json_object_seed(size_t seed);
size_t json_object_size(const json_t *object);
json_t *json_object_get(const json_t *object, const char *key);
json_t *json_object_get(const json_t *object, const char *key)
JANSSON_ATTRS((warn_unused_result));
json_t *json_object_getn(const json_t *object, const char *key, size_t key_len)
JANSSON_ATTRS((warn_unused_result));
int json_object_set_new(json_t *object, const char *key, json_t *value);
int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value);
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len,
json_t *value);
int json_object_del(json_t *object, const char *key);
int json_object_deln(json_t *object, const char *key, size_t key_len);
int json_object_clear(json_t *object);
int json_object_update(json_t *object, json_t *other);
int json_object_update_existing(json_t *object, json_t *other);
int json_object_update_missing(json_t *object, json_t *other);
int json_object_update_recursive(json_t *object, json_t *other);
void *json_object_iter(json_t *object);
void *json_object_iter_at(json_t *object, const char *key);
void *json_object_key_to_iter(const char *key);
void *json_object_iter_next(json_t *object, void *iter);
const char *json_object_iter_key(void *iter);
size_t json_object_iter_key_len(void *iter);
json_t *json_object_iter_value(void *iter);
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
#define json_object_foreach(object, key, value) \
for(key = json_object_iter_key(json_object_iter(object)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
#define json_object_foreach(object, key, value) \
for (key = json_object_iter_key(json_object_iter(object)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key( \
json_object_iter_next(object, json_object_key_to_iter(key))))
#define json_array_foreach(array, index, value) \
for(index = 0; \
index < json_array_size(array) && (value = json_array_get(array, index)); \
index++)
#define json_object_keylen_foreach(object, key, key_len, value) \
for (key = json_object_iter_key(json_object_iter(object)), \
key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key( \
json_object_iter_next(object, json_object_key_to_iter(key))), \
key_len = json_object_iter_key_len(json_object_key_to_iter(key)))
static JSON_INLINE
int json_object_set(json_t *object, const char *key, json_t *value)
{
#define json_object_foreach_safe(object, n, key, value) \
for (key = json_object_iter_key(json_object_iter(object)), \
n = json_object_iter_next(object, json_object_key_to_iter(key)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(n), \
n = json_object_iter_next(object, json_object_key_to_iter(key)))
#define json_object_keylen_foreach_safe(object, n, key, key_len, value) \
for (key = json_object_iter_key(json_object_iter(object)), \
n = json_object_iter_next(object, json_object_key_to_iter(key)), \
key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(n), key_len = json_object_iter_key_len(n), \
n = json_object_iter_next(object, json_object_key_to_iter(key)))
#define json_array_foreach(array, index, value) \
for (index = 0; \
index < json_array_size(array) && (value = json_array_get(array, index)); \
index++)
static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) {
return json_object_set_new(object, key, json_incref(value));
}
static JSON_INLINE
int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
{
static JSON_INLINE int json_object_setn(json_t *object, const char *key, size_t key_len,
json_t *value) {
return json_object_setn_new(object, key, key_len, json_incref(value));
}
static JSON_INLINE int json_object_set_nocheck(json_t *object, const char *key,
json_t *value) {
return json_object_set_new_nocheck(object, key, json_incref(value));
}
static JSON_INLINE
int json_object_iter_set(json_t *object, void *iter, json_t *value)
{
static JSON_INLINE int json_object_setn_nocheck(json_t *object, const char *key,
size_t key_len, json_t *value) {
return json_object_setn_new_nocheck(object, key, key_len, json_incref(value));
}
static JSON_INLINE int json_object_iter_set(json_t *object, void *iter, json_t *value) {
return json_object_iter_set_new(object, iter, json_incref(value));
}
static JSON_INLINE int json_object_update_new(json_t *object, json_t *other) {
int ret = json_object_update(object, other);
json_decref(other);
return ret;
}
static JSON_INLINE int json_object_update_existing_new(json_t *object, json_t *other) {
int ret = json_object_update_existing(object, other);
json_decref(other);
return ret;
}
static JSON_INLINE int json_object_update_missing_new(json_t *object, json_t *other) {
int ret = json_object_update_missing(object, other);
json_decref(other);
return ret;
}
size_t json_array_size(const json_t *array);
json_t *json_array_get(const json_t *array, size_t index);
json_t *json_array_get(const json_t *array, size_t index)
JANSSON_ATTRS((warn_unused_result));
int json_array_set_new(json_t *array, size_t index, json_t *value);
int json_array_append_new(json_t *array, json_t *value);
int json_array_insert_new(json_t *array, size_t index, json_t *value);
@@ -184,21 +296,15 @@ int json_array_remove(json_t *array, size_t index);
int json_array_clear(json_t *array);
int json_array_extend(json_t *array, json_t *other);
static JSON_INLINE
int json_array_set(json_t *array, size_t ind, json_t *value)
{
static JSON_INLINE int json_array_set(json_t *array, size_t ind, json_t *value) {
return json_array_set_new(array, ind, json_incref(value));
}
static JSON_INLINE
int json_array_append(json_t *array, json_t *value)
{
static JSON_INLINE int json_array_append(json_t *array, json_t *value) {
return json_array_append_new(array, json_incref(value));
}
static JSON_INLINE
int json_array_insert(json_t *array, size_t ind, json_t *value)
{
static JSON_INLINE int json_array_insert(json_t *array, size_t ind, json_t *value) {
return json_array_insert_new(array, ind, json_incref(value));
}
@@ -217,28 +323,35 @@ int json_real_set(json_t *real, double value);
/* pack, unpack */
json_t *json_pack(const char *fmt, ...);
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...);
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap);
json_t *json_pack(const char *fmt, ...) JANSSON_ATTRS((warn_unused_result));
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
JANSSON_ATTRS((warn_unused_result));
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap)
JANSSON_ATTRS((warn_unused_result));
#define JSON_VALIDATE_ONLY 0x1
#define JSON_STRICT 0x2
#define JSON_VALIDATE_ONLY 0x1
#define JSON_STRICT 0x2
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);
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, ...)
JANSSON_ATTRS((warn_unused_result, format(printf, 1, 2)));
json_t *json_vsprintf(const char *fmt, va_list ap)
JANSSON_ATTRS((warn_unused_result, format(printf, 1, 0)));
/* equality */
int json_equal(json_t *value1, json_t *value2);
int json_equal(const json_t *value1, const json_t *value2);
/* copying */
json_t *json_copy(json_t *value);
json_t *json_deep_copy(const json_t *value);
json_t *json_copy(json_t *value) JANSSON_ATTRS((warn_unused_result));
json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS((warn_unused_result));
/* decoding */
@@ -250,31 +363,41 @@ json_t *json_deep_copy(const json_t *value);
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error);
json_t *json_loads(const char *input, size_t flags, json_error_t *error)
JANSSON_ATTRS((warn_unused_result));
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
JANSSON_ATTRS((warn_unused_result));
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
JANSSON_ATTRS((warn_unused_result));
json_t *json_loadfd(int input, size_t flags, json_error_t *error)
JANSSON_ATTRS((warn_unused_result));
json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
JANSSON_ATTRS((warn_unused_result));
json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags,
json_error_t *error) JANSSON_ATTRS((warn_unused_result));
/* encoding */
#define JSON_MAX_INDENT 0x1F
#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT)
#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_REAL_PRECISION(n) (((n) & 0x1F) << 11)
#define JSON_MAX_INDENT 0x1F
#define JSON_INDENT(n) ((n)&JSON_MAX_INDENT)
#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_REAL_PRECISION(n) (((n)&0x1F) << 11)
#define JSON_EMBED 0x10000
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
char *json_dumps(const json_t *json, size_t flags);
char *json_dumps(const json_t *json, size_t flags) JANSSON_ATTRS((warn_unused_result));
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags);
int json_dumpf(const json_t *json, FILE *output, size_t flags);
int json_dumpfd(const json_t *json, int 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);
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data,
size_t flags);
/* custom memory allocation */
@@ -282,6 +405,12 @@ typedef void *(*json_malloc_t)(size_t);
typedef void (*json_free_t)(void *);
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);
/* runtime version checking */
const char *jansson_version_str(void);
int jansson_version_cmp(int major, int minor, int micro);
#ifdef __cplusplus
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -36,4 +36,16 @@
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
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,17 +8,18 @@
#ifndef JANSSON_PRIVATE_H
#define JANSSON_PRIVATE_H
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#include "jansson.h"
#include "jansson_private_config.h"
#include "strbuffer.h"
#include <stddef.h>
#define container_of(ptr_, type_, member_) \
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
/* On some platforms, max() may already be defined */
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
@@ -27,15 +28,13 @@
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
#endif
#endif
typedef struct {
json_t json;
hashtable_t hashtable;
size_t serial;
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 {
@@ -74,26 +72,43 @@ json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
/* Error message formatting */
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, ...);
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
void jsonp_error_set(json_error_t *error, int line, int column, 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,
enum json_error_code code, const char *msg, va_list ap);
/* Locale independent string<->double conversions */
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void *jsonp_malloc(size_t size) JANSSON_ATTRS((warn_unused_result));
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);
char *jsonp_strndup(const char *str, size_t length) JANSSON_ATTRS((warn_unused_result));
char *jsonp_strdup(const char *str) JANSSON_ATTRS((warn_unused_result));
char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS((warn_unused_result));
/* Circular reference check*/
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
#define LOOP_KEY_LEN (2 + (sizeof(json_t *) * 2) + 1)
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size,
size_t *key_len_out);
/* Windows compatibility */
#ifdef _WIN32
#if defined(_WIN32) || defined(WIN32)
#if defined(_MSC_VER) /* MS compiller */
#if (_MSC_VER < 1900) && \
!defined(snprintf) /* snprintf not defined yet & not introduced */
#define snprintf _snprintf
#endif
#if (_MSC_VER < 1500) && \
!defined(vsnprintf) /* vsnprintf not defined yet & not introduced */
#define vsnprintf(b, c, f, a) _vsnprintf(b, c, f, a)
#endif
#else /* Other Windows compiller, old definition */
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
// clang-format off
/*
-------------------------------------------------------------------------------
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
@@ -72,7 +73,7 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy.
# define HASH_BIG_ENDIAN 0
#endif
#define hashsize(n) ((uint32_t)1<<(n))
#define hashsize(n) ((size_t)1<<(n))
#define hashmask(n) (hashsize(n)-1)
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
@@ -243,7 +244,7 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticably faster for short strings (like English words).
* noticeably faster for short strings (like English words).
*/
#ifndef NO_MASKING_TRICK
@@ -359,17 +360,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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
*
* Jansson is free software; you can redistribute it and/or modify it
@@ -20,33 +20,27 @@
static json_malloc_t do_malloc = malloc;
static json_free_t do_free = free;
void *jsonp_malloc(size_t size)
{
if(!size)
void *jsonp_malloc(size_t size) {
if (!size)
return NULL;
return (*do_malloc)(size);
}
void jsonp_free(void *ptr)
{
if(!ptr)
void jsonp_free(void *ptr) {
if (!ptr)
return;
(*do_free)(ptr);
}
char *jsonp_strdup(const char *str)
{
return jsonp_strndup(str, strlen(str));
}
char *jsonp_strdup(const char *str) { return jsonp_strndup(str, strlen(str)); }
char *jsonp_strndup(const char *str, size_t len)
{
char *jsonp_strndup(const char *str, size_t len) {
char *new_str;
new_str = jsonp_malloc(len + 1);
if(!new_str)
if (!new_str)
return NULL;
memcpy(new_str, str, len);
@@ -54,8 +48,14 @@ char *jsonp_strndup(const char *str, size_t len)
return new_str;
}
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
{
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) {
do_malloc = malloc_fn;
do_free = free_fn;
}
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn) {
if (malloc_fn)
*malloc_fn = do_malloc;
if (free_fn)
*free_fn = do_free;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -9,22 +9,21 @@
#define _GNU_SOURCE
#endif
#include "strbuffer.h"
#include "jansson_private.h"
#include <stdlib.h>
#include <string.h>
#include "jansson_private.h"
#include "strbuffer.h"
#define STRBUFFER_MIN_SIZE 16
#define STRBUFFER_FACTOR 2
#define STRBUFFER_SIZE_MAX ((size_t)-1)
#define STRBUFFER_MIN_SIZE 16
#define STRBUFFER_FACTOR 2
#define STRBUFFER_SIZE_MAX ((size_t)-1)
int strbuffer_init(strbuffer_t *strbuff)
{
int strbuffer_init(strbuffer_t *strbuff) {
strbuff->size = STRBUFFER_MIN_SIZE;
strbuff->length = 0;
strbuff->value = jsonp_malloc(strbuff->size);
if(!strbuff->value)
if (!strbuff->value)
return -1;
/* initialize to empty */
@@ -32,9 +31,8 @@ int strbuffer_init(strbuffer_t *strbuff)
return 0;
}
void strbuffer_close(strbuffer_t *strbuff)
{
if(strbuff->value)
void strbuffer_close(strbuffer_t *strbuff) {
if (strbuff->value)
jsonp_free(strbuff->value);
strbuff->size = 0;
@@ -42,52 +40,38 @@ void strbuffer_close(strbuffer_t *strbuff)
strbuff->value = NULL;
}
void strbuffer_clear(strbuffer_t *strbuff)
{
void strbuffer_clear(strbuffer_t *strbuff) {
strbuff->length = 0;
strbuff->value[0] = '\0';
}
const char *strbuffer_value(const strbuffer_t *strbuff)
{
return strbuff->value;
}
const char *strbuffer_value(const strbuffer_t *strbuff) { return strbuff->value; }
char *strbuffer_steal_value(strbuffer_t *strbuff)
{
char *strbuffer_steal_value(strbuffer_t *strbuff) {
char *result = strbuff->value;
strbuff->value = NULL;
return result;
}
int strbuffer_append(strbuffer_t *strbuff, const char *string)
{
return strbuffer_append_bytes(strbuff, string, strlen(string));
}
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
{
int strbuffer_append_byte(strbuffer_t *strbuff, char byte) {
return strbuffer_append_bytes(strbuff, &byte, 1);
}
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
{
if(size >= strbuff->size - strbuff->length)
{
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) {
if (size >= strbuff->size - strbuff->length) {
size_t new_size;
char *new_value;
/* avoid integer overflow */
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|| size > STRBUFFER_SIZE_MAX - 1
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR ||
size > STRBUFFER_SIZE_MAX - 1 ||
strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
return -1;
new_size = max(strbuff->size * STRBUFFER_FACTOR,
strbuff->length + size + 1);
new_size = max(strbuff->size * STRBUFFER_FACTOR, strbuff->length + size + 1);
new_value = jsonp_malloc(new_size);
if(!new_value)
if (!new_value)
return -1;
memcpy(new_value, strbuff->value, strbuff->length);
@@ -104,13 +88,11 @@ int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
return 0;
}
char strbuffer_pop(strbuffer_t *strbuff)
{
if(strbuff->length > 0) {
char strbuffer_pop(strbuffer_t *strbuff) {
if (strbuff->length > 0) {
char c = strbuff->value[--strbuff->length];
strbuff->value[strbuff->length] = '\0';
return c;
}
else
} else
return '\0';
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,13 +8,16 @@
#ifndef STRBUFFER_H
#define STRBUFFER_H
#include "jansson.h"
#include <stdlib.h>
typedef struct {
char *value;
size_t length; /* bytes used */
size_t size; /* bytes allocated */
size_t length; /* bytes used */
size_t size; /* bytes allocated */
} strbuffer_t;
int strbuffer_init(strbuffer_t *strbuff);
int strbuffer_init(strbuffer_t *strbuff) JANSSON_ATTRS((warn_unused_result));
void strbuffer_close(strbuffer_t *strbuff);
void strbuffer_clear(strbuffer_t *strbuff);
@@ -24,7 +27,6 @@ const char *strbuffer_value(const strbuffer_t *strbuff);
/* Steal the value and close the strbuffer */
char *strbuffer_steal_value(strbuffer_t *strbuff);
int strbuffer_append(strbuffer_t *strbuff, const char *string);
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);

View File

@@ -1,10 +1,10 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "jansson_private.h"
#include "strbuffer.h"
#include <assert.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
/* need jansson_private_config.h to get the correct snprintf */
#ifdef HAVE_CONFIG_H
@@ -24,41 +24,38 @@
this way. Multi-threaded programs should use uselocale() instead.
*/
static void to_locale(strbuffer_t *strbuffer)
{
static void to_locale(strbuffer_t *strbuffer) {
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
if (*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
if (pos)
*pos = *point;
}
static void from_locale(char *buffer)
{
static void from_locale(char *buffer) {
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
if (*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
if (pos)
*pos = '.';
}
#endif
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
{
int jsonp_strtod(strbuffer_t *strbuffer, double *out) {
double value;
char *end;
@@ -70,7 +67,7 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
value = strtod(strbuffer->value, &end);
assert(end == strbuffer->value + strbuffer->length);
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
if ((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
/* Overflow */
return -1;
}
@@ -79,8 +76,7 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
return 0;
}
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
{
int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
int ret;
char *start, *end;
size_t length;
@@ -89,11 +85,11 @@ int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
precision = 17;
ret = snprintf(buffer, size, "%.*g", precision, value);
if(ret < 0)
if (ret < 0)
return -1;
length = (size_t)ret;
if(length >= size)
if (length >= size)
return -1;
#if JSON_HAVE_LOCALECONV
@@ -102,10 +98,8 @@ int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
/* Make sure there's a dot or 'e' in the output. Otherwise
a real is converted to an integer when decoding */
if(strchr(buffer, '.') == NULL &&
strchr(buffer, 'e') == NULL)
{
if(length + 3 >= size) {
if (strchr(buffer, '.') == NULL && strchr(buffer, 'e') == NULL) {
if (length + 3 >= size) {
/* No space to append ".0" */
return -1;
}
@@ -118,17 +112,17 @@ int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
/* Remove leading '+' from positive exponent. Also remove leading
zeros from exponents (added by some printf() implementations) */
start = strchr(buffer, 'e');
if(start) {
if (start) {
start++;
end = start + 1;
if(*start == '-')
if (*start == '-')
start++;
while(*end == '0')
while (*end == '0')
end++;
if(end != start) {
if (end != start) {
memmove(start, end, length - (size_t)(end - buffer));
length -= (size_t)(end - start);
}

112
src/utf.c
View File

@@ -1,111 +1,89 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include "utf.h"
#include <string.h>
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
{
if(codepoint < 0)
int utf8_encode(int32_t codepoint, char *buffer, size_t *size) {
if (codepoint < 0)
return -1;
else if(codepoint < 0x80)
{
else if (codepoint < 0x80) {
buffer[0] = (char)codepoint;
*size = 1;
}
else if(codepoint < 0x800)
{
} else if (codepoint < 0x800) {
buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6);
buffer[1] = 0x80 + ((codepoint & 0x03F));
*size = 2;
}
else if(codepoint < 0x10000)
{
} else if (codepoint < 0x10000) {
buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12);
buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6);
buffer[2] = 0x80 + ((codepoint & 0x003F));
*size = 3;
}
else if(codepoint <= 0x10FFFF)
{
} else if (codepoint <= 0x10FFFF) {
buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18);
buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12);
buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6);
buffer[3] = 0x80 + ((codepoint & 0x00003F));
*size = 4;
}
else
} else
return -1;
return 0;
}
size_t utf8_check_first(char byte)
{
size_t utf8_check_first(char byte) {
unsigned char u = (unsigned char)byte;
if(u < 0x80)
if (u < 0x80)
return 1;
if(0x80 <= u && u <= 0xBF) {
if (0x80 <= u && u <= 0xBF) {
/* second, third or fourth byte of a multi-byte
sequence, i.e. a "continuation byte" */
return 0;
}
else if(u == 0xC0 || u == 0xC1) {
} else if (u == 0xC0 || u == 0xC1) {
/* overlong encoding of an ASCII byte */
return 0;
}
else if(0xC2 <= u && u <= 0xDF) {
} else if (0xC2 <= u && u <= 0xDF) {
/* 2-byte sequence */
return 2;
}
else if(0xE0 <= u && u <= 0xEF) {
else if (0xE0 <= u && u <= 0xEF) {
/* 3-byte sequence */
return 3;
}
else if(0xF0 <= u && u <= 0xF4) {
} else if (0xF0 <= u && u <= 0xF4) {
/* 4-byte sequence */
return 4;
}
else { /* u >= 0xF5 */
} else { /* u >= 0xF5 */
/* Restricted (start of 4-, 5- or 6-byte sequence) or invalid
UTF-8 */
return 0;
}
}
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
{
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint) {
size_t i;
int32_t value = 0;
unsigned char u = (unsigned char)buffer[0];
if(size == 2)
{
if (size == 2) {
value = u & 0x1F;
}
else if(size == 3)
{
} else if (size == 3) {
value = u & 0xF;
}
else if(size == 4)
{
} else if (size == 4) {
value = u & 0x7;
}
else
} else
return 0;
for(i = 1; i < size; i++)
{
for (i = 1; i < size; i++) {
u = (unsigned char)buffer[i];
if(u < 0x80 || u > 0xBF) {
if (u < 0x80 || u > 0xBF) {
/* not a continuation byte */
return 0;
}
@@ -113,70 +91,64 @@ size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
value = (value << 6) + (u & 0x3F);
}
if(value > 0x10FFFF) {
if (value > 0x10FFFF) {
/* not in Unicode range */
return 0;
}
else if(0xD800 <= value && value <= 0xDFFF) {
else if (0xD800 <= value && value <= 0xDFFF) {
/* invalid code point (UTF-16 surrogate halves) */
return 0;
}
else if((size == 2 && value < 0x80) ||
(size == 3 && value < 0x800) ||
(size == 4 && value < 0x10000)) {
else if ((size == 2 && value < 0x80) || (size == 3 && value < 0x800) ||
(size == 4 && value < 0x10000)) {
/* overlong encoding */
return 0;
}
if(codepoint)
if (codepoint)
*codepoint = value;
return 1;
}
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
{
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint) {
size_t count;
int32_t value;
if(!bufsize)
if (!bufsize)
return buffer;
count = utf8_check_first(buffer[0]);
if(count <= 0)
if (count <= 0)
return NULL;
if(count == 1)
if (count == 1)
value = (unsigned char)buffer[0];
else
{
if(count > bufsize || !utf8_check_full(buffer, count, &value))
else {
if (count > bufsize || !utf8_check_full(buffer, count, &value))
return NULL;
}
if(codepoint)
if (codepoint)
*codepoint = value;
return buffer + count;
}
int utf8_check_string(const char *string, size_t length)
{
int utf8_check_string(const char *string, size_t length) {
size_t i;
for(i = 0; i < length; i++)
{
for (i = 0; i < length; i++) {
size_t count = utf8_check_first(string[i]);
if(count == 0)
if (count == 0)
return 0;
else if(count > 1)
{
if(count > length - i)
else if (count > 1) {
if (count > length - i)
return 0;
if(!utf8_check_full(&string[i], count, NULL))
if (!utf8_check_full(&string[i], count, NULL))
return 0;
i += count - 1;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -12,6 +12,7 @@
#include <jansson_private_config.h>
#endif
#include <stddef.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif

File diff suppressed because it is too large Load Diff

28
src/version.c Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2019 Sean Bright <sean.bright@gmail.com>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "jansson.h"
const char *jansson_version_str(void) { return JANSSON_VERSION; }
int jansson_version_cmp(int major, int minor, int micro) {
int diff;
if ((diff = JANSSON_MAJOR_VERSION - major)) {
return diff;
}
if ((diff = JANSSON_MINOR_VERSION - minor)) {
return diff;
}
return JANSSON_MICRO_VERSION - micro;
}

5
test/.gitignore vendored
View File

@@ -1,17 +1,20 @@
logs
bin/json_process
suites/api/test_array
suites/api/test_chaos
suites/api/test_copy
suites/api/test_cpp
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
suites/api/test_version

View File

@@ -1,4 +1,4 @@
SUBDIRS = bin suites
SUBDIRS = bin suites ossfuzz
EXTRA_DIST = scripts run-suites
TESTS = run-suites

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -9,26 +9,25 @@
#include <jansson_private_config.h>
#endif
#include <ctype.h>
#include <jansson.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#endif
#if _WIN32
#include <io.h> /* for _setmode() */
#include <fcntl.h> /* for _O_BINARY */
#include <fcntl.h> /* for _O_BINARY */
#include <io.h> /* for _setmode() */
static const char dir_sep = '\\';
#else
static const char dir_sep = '/';
#endif
struct config {
int indent;
int compact;
@@ -47,8 +46,7 @@ struct config {
/* Return a pointer to the first non-whitespace character of str.
Modifies str so that all trailing whitespace characters are
replaced by '\0'. */
static const char *strip(char *str)
{
static const char *strip(char *str) {
size_t length;
char *result = str;
while (*result && l_isspace(*result))
@@ -64,17 +62,15 @@ static const char *strip(char *str)
return result;
}
static char *loadfile(FILE *file)
{
long fsize, ret;
static char *loadfile(FILE *file) {
size_t fsize, ret;
char *buf;
fseek(file, 0, SEEK_END);
fsize = ftell(file);
fseek(file, 0, SEEK_SET);
buf = malloc(fsize+1);
buf = malloc(fsize + 1);
ret = fread(buf, 1, fsize, file);
if (ret != fsize)
exit(1);
@@ -83,9 +79,7 @@ static char *loadfile(FILE *file)
return buf;
}
static void read_conf(FILE *conffile)
{
static void read_conf(FILE *conffile) {
char *buffer, *line, *val;
buffer = loadfile(conffile);
@@ -124,9 +118,7 @@ static void read_conf(FILE *conffile)
free(buffer);
}
static int cmpfile(const char *str, const char *path, const char *fname)
{
static int cmpfile(const char *str, const char *path, const char *fname) {
char filename[1024], *buffer;
int ret;
FILE *file;
@@ -156,8 +148,7 @@ static int cmpfile(const char *str, const char *path, const char *fname)
return ret;
}
int use_conf(char *test_path)
{
int use_conf(char *test_path) {
int ret;
size_t flags = 0;
char filename[1024], errstr[1024];
@@ -200,8 +191,7 @@ int use_conf(char *test_path)
flags |= JSON_SORT_KEYS;
if (conf.precision < 0 || conf.precision > 31) {
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
conf.precision);
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n", conf.precision);
fclose(infile);
return 2;
}
@@ -216,15 +206,13 @@ int use_conf(char *test_path)
buffer = loadfile(infile);
json = json_loads(strip(buffer), 0, &error);
free(buffer);
}
else
} else
json = json_loadf(infile, 0, &error);
fclose(infile);
if (!json) {
sprintf(errstr, "%d %d %d\n%s\n",
error.line, error.column, error.position,
sprintf(errstr, "%d %d %d\n%s\n", error.line, error.column, error.position,
error.text);
ret = cmpfile(errstr, test_path, "error");
@@ -239,80 +227,77 @@ int use_conf(char *test_path)
return ret;
}
static int getenv_int(const char *name)
{
static int getenv_int(const char *name) {
char *value, *end;
long result;
value = getenv(name);
if(!value)
if (!value)
return 0;
result = strtol(value, &end, 10);
if(*end != '\0')
if (*end != '\0')
return 0;
return (int)result;
}
int use_env()
{
int use_env() {
int indent, precision;
size_t flags = 0;
json_t *json;
json_error_t error;
#ifdef _WIN32
#ifdef _WIN32
/* On Windows, set stdout and stderr to binary mode to avoid
outputting DOS line terminators */
_setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stderr), _O_BINARY);
#endif
#endif
indent = getenv_int("JSON_INDENT");
if(indent < 0 || indent > 31) {
if (indent < 0 || indent > 31) {
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
return 2;
}
if(indent > 0)
if (indent > 0)
flags |= JSON_INDENT(indent);
if(getenv_int("JSON_COMPACT") > 0)
if (getenv_int("JSON_COMPACT") > 0)
flags |= JSON_COMPACT;
if(getenv_int("JSON_ENSURE_ASCII"))
if (getenv_int("JSON_ENSURE_ASCII"))
flags |= JSON_ENSURE_ASCII;
if(getenv_int("JSON_PRESERVE_ORDER"))
if (getenv_int("JSON_PRESERVE_ORDER"))
flags |= JSON_PRESERVE_ORDER;
if(getenv_int("JSON_SORT_KEYS"))
if (getenv_int("JSON_SORT_KEYS"))
flags |= JSON_SORT_KEYS;
precision = getenv_int("JSON_REAL_PRECISION");
if(precision < 0 || precision > 31) {
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
precision);
if (precision < 0 || precision > 31) {
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n", precision);
return 2;
}
if(getenv("HASHSEED"))
if (getenv("HASHSEED"))
json_object_seed(getenv_int("HASHSEED"));
if(precision > 0)
if (precision > 0)
flags |= JSON_REAL_PRECISION(precision);
if(getenv_int("STRIP")) {
if (getenv_int("STRIP")) {
/* Load to memory, strip leading and trailing whitespace */
size_t size = 0, used = 0;
char *buffer = NULL, *buf_ck = NULL;
while(1) {
while (1) {
size_t count;
size = (size == 0 ? 128 : size * 2);
buf_ck = realloc(buffer, size);
if(!buf_ck) {
if (!buf_ck) {
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
free(buffer);
return 1;
@@ -320,7 +305,7 @@ int use_env()
buffer = buf_ck;
count = fread(buffer + used, 1, size - used, stdin);
if(count < size - used) {
if (count < size - used) {
buffer[used + count] = '\0';
break;
}
@@ -329,14 +314,12 @@ int use_env()
json = json_loads(strip(buffer), 0, &error);
free(buffer);
}
else
} else
json = json_loadf(stdin, 0, &error);
if(!json) {
fprintf(stderr, "%d %d %d\n%s\n",
error.line, error.column,
error.position, error.text);
if (!json) {
fprintf(stderr, "%d %d %d\n%s\n", error.line, error.column, error.position,
error.text);
return 1;
}
@@ -346,14 +329,13 @@ int use_env()
return 0;
}
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
int i;
char *test_path = NULL;
#ifdef HAVE_SETLOCALE
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
#endif
if (argc < 2) {
goto usage;
@@ -370,8 +352,7 @@ int main(int argc, char *argv[])
if (conf.use_env)
return use_env();
else
{
else {
if (!test_path)
goto usage;

1
test/ossfuzz/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
json_load_dump_fuzzer

32
test/ossfuzz/Makefile.am Normal file
View File

@@ -0,0 +1,32 @@
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
LDADD = $(top_builddir)/src/libjansson.la
if USE_OSSFUZZ_FLAG
FUZZ_FLAG = $(LIB_FUZZING_ENGINE)
else
if USE_OSSFUZZ_STATIC
LDADD += $(LIB_FUZZING_ENGINE)
FUZZ_FLAG =
else
LDADD += libstandaloneengine.a
FUZZ_FLAG =
endif
endif
noinst_PROGRAMS =
noinst_LIBRARIES =
if USE_OSSFUZZERS
noinst_PROGRAMS += \
json_load_dump_fuzzer
noinst_LIBRARIES += \
libstandaloneengine.a
endif
json_load_dump_fuzzer_SOURCES = json_load_dump_fuzzer.cc testinput.h
json_load_dump_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG)
json_load_dump_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static
libstandaloneengine_a_SOURCES = standaloneengine.cc
libstandaloneengine_a_CXXFLAGS = $(AM_CXXFLAGS)

View File

@@ -0,0 +1,132 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <inttypes.h>
#include "jansson.h"
static int enable_diags;
#define FUZZ_DEBUG(FMT, ...) \
if (enable_diags) \
{ \
fprintf(stderr, FMT, ##__VA_ARGS__); \
fprintf(stderr, "\n"); \
}
static int json_dump_counter(const char *buffer, size_t size, void *data)
{
uint64_t *counter = reinterpret_cast<uint64_t *>(data);
*counter += size;
return 0;
}
#define NUM_COMMAND_BYTES (sizeof(size_t) + sizeof(size_t) + 1)
#define FUZZ_DUMP_CALLBACK 0x00
#define FUZZ_DUMP_STRING 0x01
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
json_error_t error;
unsigned char dump_mode;
// Enable or disable diagnostics based on the FUZZ_VERBOSE environment flag.
enable_diags = (getenv("FUZZ_VERBOSE") != NULL);
FUZZ_DEBUG("Input data length: %zd", size);
if (size < NUM_COMMAND_BYTES)
{
return 0;
}
// Use the first sizeof(size_t) bytes as load flags.
size_t load_flags = *(const size_t*)data;
data += sizeof(size_t);
FUZZ_DEBUG("load_flags: 0x%zx\n"
"& JSON_REJECT_DUPLICATES = 0x%zx\n"
"& JSON_DECODE_ANY = 0x%zx\n"
"& JSON_DISABLE_EOF_CHECK = 0x%zx\n"
"& JSON_DECODE_INT_AS_REAL = 0x%zx\n"
"& JSON_ALLOW_NUL = 0x%zx\n",
load_flags,
load_flags & JSON_REJECT_DUPLICATES,
load_flags & JSON_DECODE_ANY,
load_flags & JSON_DISABLE_EOF_CHECK,
load_flags & JSON_DECODE_INT_AS_REAL,
load_flags & JSON_ALLOW_NUL);
// Use the next sizeof(size_t) bytes as dump flags.
size_t dump_flags = *(const size_t*)data;
data += sizeof(size_t);
FUZZ_DEBUG("dump_flags: 0x%zx\n"
"& JSON_MAX_INDENT = 0x%zx\n"
"& JSON_COMPACT = 0x%zx\n"
"& JSON_ENSURE_ASCII = 0x%zx\n"
"& JSON_SORT_KEYS = 0x%zx\n"
"& JSON_PRESERVE_ORDER = 0x%zx\n"
"& JSON_ENCODE_ANY = 0x%zx\n"
"& JSON_ESCAPE_SLASH = 0x%zx\n"
"& JSON_REAL_PRECISION = 0x%zx\n"
"& JSON_EMBED = 0x%zx\n",
dump_flags,
dump_flags & JSON_MAX_INDENT,
dump_flags & JSON_COMPACT,
dump_flags & JSON_ENSURE_ASCII,
dump_flags & JSON_SORT_KEYS,
dump_flags & JSON_PRESERVE_ORDER,
dump_flags & JSON_ENCODE_ANY,
dump_flags & JSON_ESCAPE_SLASH,
((dump_flags >> 11) & 0x1F) << 11,
dump_flags & JSON_EMBED);
// Use the next byte as the dump mode.
dump_mode = data[0];
data++;
FUZZ_DEBUG("dump_mode: 0x%x", (unsigned int)dump_mode);
// Remove the command bytes from the size total.
size -= NUM_COMMAND_BYTES;
// Attempt to load the remainder of the data with the given load flags.
const char* text = reinterpret_cast<const char *>(data);
json_t* jobj = json_loadb(text, size, load_flags, &error);
if (jobj == NULL)
{
return 0;
}
if (dump_mode & FUZZ_DUMP_STRING)
{
// Dump as a string. Remove indents so that we don't run out of memory.
char *out = json_dumps(jobj, dump_flags & ~JSON_MAX_INDENT);
if (out != NULL)
{
free(out);
}
}
else
{
// Default is callback mode.
//
// Attempt to dump the loaded json object with the given dump flags.
uint64_t counter = 0;
json_dump_callback(jobj, json_dump_counter, &counter, dump_flags);
FUZZ_DEBUG("Counter function counted %" PRIu64 " bytes.", counter);
}
if (jobj)
{
json_decref(jobj);
}
return 0;
}

30
test/ossfuzz/ossfuzz.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash -eu
# This script is called by the oss-fuzz main project when compiling the fuzz
# targets. This script is regression tested by travisoss.sh.
# Save off the current folder as the build root.
export BUILD_ROOT=$PWD
echo "CC: $CC"
echo "CXX: $CXX"
echo "LIB_FUZZING_ENGINE: $LIB_FUZZING_ENGINE"
echo "CFLAGS: $CFLAGS"
echo "CXXFLAGS: $CXXFLAGS"
echo "OUT: $OUT"
export MAKEFLAGS+="-j$(nproc)"
# Install dependencies
apt-get -y install automake libtool
# Compile the fuzzer.
autoreconf -i
./configure --enable-ossfuzzers
make
# Copy the fuzzer to the output directory.
cp -v test/ossfuzz/json_load_dump_fuzzer $OUT/
# Zip up all input files to use as a test corpus
find test/suites -name "input" -print | zip $OUT/json_load_dump_fuzzer_seed_corpus.zip -@

View File

@@ -0,0 +1,74 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "testinput.h"
/**
* Main procedure for standalone fuzzing engine.
*
* Reads filenames from the argument array. For each filename, read the file
* into memory and then call the fuzzing interface with the data.
*/
int main(int argc, char **argv)
{
int ii;
for(ii = 1; ii < argc; ii++)
{
FILE *infile;
printf("[%s] ", argv[ii]);
/* Try and open the file. */
infile = fopen(argv[ii], "rb");
if(infile)
{
uint8_t *buffer = NULL;
size_t buffer_len;
printf("Opened.. ");
/* Get the length of the file. */
fseek(infile, 0L, SEEK_END);
buffer_len = ftell(infile);
/* Reset the file indicator to the beginning of the file. */
fseek(infile, 0L, SEEK_SET);
/* Allocate a buffer for the file contents. */
buffer = (uint8_t *)calloc(buffer_len, sizeof(uint8_t));
if(buffer)
{
/* Read all the text from the file into the buffer. */
fread(buffer, sizeof(uint8_t), buffer_len, infile);
printf("Read %zu bytes, fuzzing.. ", buffer_len);
/* Call the fuzzer with the data. */
LLVMFuzzerTestOneInput(buffer, buffer_len);
printf("complete !!");
/* Free the buffer as it's no longer needed. */
free(buffer);
buffer = NULL;
}
else
{
fprintf(stderr,
"[%s] Failed to allocate %zu bytes \n",
argv[ii],
buffer_len);
}
/* Close the file as it's no longer needed. */
fclose(infile);
infile = NULL;
}
else
{
/* Failed to open the file. Maybe wrong name or wrong permissions? */
fprintf(stderr, "[%s] Open failed. \n", argv[ii]);
}
printf("\n");
}
}

3
test/ossfuzz/testinput.h Normal file
View File

@@ -0,0 +1,3 @@
#include <inttypes.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

View File

@@ -34,15 +34,15 @@ failed=0
for suite in $SUITES; do
echo "Suite: $suite"
if $suites_srcdir/$suite/run $suite; then
passed=$(($passed+1))
passed=`expr $passed + 1`
else
failed=$(($failed+1))
failed=`expr $failed + 1`
[ $STOP -eq 1 ] && break
fi
done
if [ $failed -gt 0 ]; then
echo "$failed of $((passed+failed)) test suites failed"
echo "$failed of `expr $passed + $failed` test suites failed"
exit 1
else
echo "$passed test suites passed"

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.

View File

@@ -2,24 +2,30 @@ EXTRA_DIST = run check-exports
check_PROGRAMS = \
test_array \
test_chaos \
test_copy \
test_dump \
test_dump_callback \
test_equal \
test_fixed_size \
test_load \
test_loadb \
test_load_callback \
test_loadb \
test_memory_funcs \
test_number \
test_object \
test_pack \
test_simple \
test_unpack
test_sprintf \
test_unpack \
test_version
test_array_SOURCES = test_array.c util.h
test_chaos_SOURCES = test_chaos.c util.h
test_copy_SOURCES = test_copy.c util.h
test_dump_SOURCES = test_dump.c util.h
test_dump_callback_SOURCES = test_dump_callback.c util.h
test_fixed_size_SOURCES = test_fixed_size.c util.h
test_load_SOURCES = test_load.c util.h
test_loadb_SOURCES = test_loadb.c util.h
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
@@ -27,7 +33,9 @@ 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
test_version_SOURCES = test_version.c util.h
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
LDFLAGS = -static # for speed and Valgrind

View File

@@ -7,7 +7,7 @@ SOFILE="../src/.libs/libjansson.so"
# The list of symbols, which the shared object should export, is read
# from the def file, which is used in Windows builds
grep 'json_' $top_srcdir/src/jansson.def \
grep 'json_\|jansson_' $top_srcdir/src/jansson.def \
| sed -e 's/ //g' \
| sort \
>$test_log/exports
@@ -15,7 +15,7 @@ grep 'json_' $top_srcdir/src/jansson.def \
nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \
|| exit 77 # Skip if "nm -D" doesn't seem to work
grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | grep -v '^_' | sort >$test_log/output
grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | grep -v '^_' | sed 's/@@libjansson.*//' | sort >$test_log/output
if ! cmp -s $test_log/exports $test_log/output; then
diff -u $test_log/exports $test_log/output >&2

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.

View File

@@ -1,15 +1,14 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include "util.h"
#include <jansson.h>
static void test_misc(void)
{
static void test_misc(void) {
json_t *array, *five, *seven, *value;
size_t i;
@@ -17,96 +16,96 @@ static void test_misc(void)
five = json_integer(5);
seven = json_integer(7);
if(!array)
if (!array)
fail("unable to create array");
if(!five || !seven)
if (!five || !seven)
fail("unable to create integer");
if(json_array_size(array) != 0)
if (json_array_size(array) != 0)
fail("empty array has nonzero size");
if(!json_array_append(array, NULL))
if (!json_array_append(array, NULL))
fail("able to append NULL");
if(json_array_append(array, five))
if (json_array_append(array, five))
fail("unable to append");
if(json_array_size(array) != 1)
if (json_array_size(array) != 1)
fail("wrong array size");
value = json_array_get(array, 0);
if(!value)
if (!value)
fail("unable to get item");
if(value != five)
if (value != five)
fail("got wrong value");
if(json_array_append(array, seven))
if (json_array_append(array, seven))
fail("unable to append value");
if(json_array_size(array) != 2)
if (json_array_size(array) != 2)
fail("wrong array size");
value = json_array_get(array, 1);
if(!value)
if (!value)
fail("unable to get item");
if(value != seven)
if (value != seven)
fail("got wrong value");
if(json_array_set(array, 0, seven))
if (json_array_set(array, 0, seven))
fail("unable to set value");
if(!json_array_set(array, 0, NULL))
if (!json_array_set(array, 0, NULL))
fail("able to set NULL");
if(json_array_size(array) != 2)
if (json_array_size(array) != 2)
fail("wrong array size");
value = json_array_get(array, 0);
if(!value)
if (!value)
fail("unable to get item");
if(value != seven)
if (value != seven)
fail("got wrong value");
if(json_array_get(array, 2) != NULL)
if (json_array_get(array, 2) != NULL)
fail("able to get value out of bounds");
if(!json_array_set(array, 2, seven))
if (!json_array_set(array, 2, seven))
fail("able to set value out of bounds");
for(i = 2; i < 30; i++) {
if(json_array_append(array, seven))
for (i = 2; i < 30; i++) {
if (json_array_append(array, seven))
fail("unable to append value");
if(json_array_size(array) != i + 1)
if (json_array_size(array) != i + 1)
fail("wrong array size");
}
for(i = 0; i < 30; i++) {
for (i = 0; i < 30; i++) {
value = json_array_get(array, i);
if(!value)
if (!value)
fail("unable to get item");
if(value != seven)
if (value != seven)
fail("got wrong value");
}
if(json_array_set_new(array, 15, json_integer(123)))
if (json_array_set_new(array, 15, json_integer(123)))
fail("unable to set new value");
value = json_array_get(array, 15);
if(!json_is_integer(value) || json_integer_value(value) != 123)
if (!json_is_integer(value) || json_integer_value(value) != 123)
fail("json_array_set_new works incorrectly");
if(!json_array_set_new(array, 15, NULL))
if (!json_array_set_new(array, 15, NULL))
fail("able to set_new NULL value");
if(json_array_append_new(array, json_integer(321)))
if (json_array_append_new(array, json_integer(321)))
fail("unable to append new value");
value = json_array_get(array, json_array_size(array) - 1);
if(!json_is_integer(value) || json_integer_value(value) != 321)
if (!json_is_integer(value) || json_integer_value(value) != 321)
fail("json_array_append_new works incorrectly");
if(!json_array_append_new(array, NULL))
if (!json_array_append_new(array, NULL))
fail("able to append_new NULL value");
json_decref(five);
@@ -114,8 +113,7 @@ static void test_misc(void)
json_decref(array);
}
static void test_insert(void)
{
static void test_insert(void) {
json_t *array, *five, *seven, *eleven, *value;
int i;
@@ -124,77 +122,71 @@ static void test_insert(void)
seven = json_integer(7);
eleven = json_integer(11);
if(!array)
if (!array)
fail("unable to create array");
if(!five || !seven || !eleven)
if (!five || !seven || !eleven)
fail("unable to create integer");
if(!json_array_insert(array, 1, five))
if (!json_array_insert(array, 1, five))
fail("able to insert value out of bounds");
if(json_array_insert(array, 0, five))
if (json_array_insert(array, 0, five))
fail("unable to insert value in an empty array");
if(json_array_get(array, 0) != five)
if (json_array_get(array, 0) != five)
fail("json_array_insert works incorrectly");
if(json_array_size(array) != 1)
if (json_array_size(array) != 1)
fail("array size is invalid after insertion");
if(json_array_insert(array, 1, seven))
if (json_array_insert(array, 1, seven))
fail("unable to insert value at the end of an array");
if(json_array_get(array, 0) != five)
if (json_array_get(array, 0) != five)
fail("json_array_insert works incorrectly");
if(json_array_get(array, 1) != seven)
if (json_array_get(array, 1) != seven)
fail("json_array_insert works incorrectly");
if(json_array_size(array) != 2)
if (json_array_size(array) != 2)
fail("array size is invalid after insertion");
if(json_array_insert(array, 1, eleven))
if (json_array_insert(array, 1, eleven))
fail("unable to insert value in the middle of an array");
if(json_array_get(array, 0) != five)
if (json_array_get(array, 0) != five)
fail("json_array_insert works incorrectly");
if(json_array_get(array, 1) != eleven)
if (json_array_get(array, 1) != eleven)
fail("json_array_insert works incorrectly");
if(json_array_get(array, 2) != seven)
if (json_array_get(array, 2) != seven)
fail("json_array_insert works incorrectly");
if(json_array_size(array) != 3)
if (json_array_size(array) != 3)
fail("array size is invalid after insertion");
if(json_array_insert_new(array, 2, json_integer(123)))
if (json_array_insert_new(array, 2, json_integer(123)))
fail("unable to insert value in the middle of an array");
value = json_array_get(array, 2);
if(!json_is_integer(value) || json_integer_value(value) != 123)
if (!json_is_integer(value) || json_integer_value(value) != 123)
fail("json_array_insert_new works incorrectly");
if(json_array_size(array) != 4)
if (json_array_size(array) != 4)
fail("array size is invalid after insertion");
for(i = 0; i < 20; i++) {
if(json_array_insert(array, 0, seven))
fail("unable to insert value at the begining of an array");
for (i = 0; i < 20; i++) {
if (json_array_insert(array, 0, seven))
fail("unable to insert value at the beginning of an array");
}
for(i = 0; i < 20; i++) {
if(json_array_get(array, i) != seven)
for (i = 0; i < 20; i++) {
if (json_array_get(array, i) != seven)
fail("json_aray_insert works incorrectly");
}
if(json_array_size(array) != 24)
if (json_array_size(array) != 24)
fail("array size is invalid after loop insertion");
json_decref(five);
@@ -203,8 +195,7 @@ static void test_insert(void)
json_decref(array);
}
static void test_remove(void)
{
static void test_remove(void) {
json_t *array, *five, *seven;
int i;
@@ -212,56 +203,50 @@ static void test_remove(void)
five = json_integer(5);
seven = json_integer(7);
if(!array)
if (!array)
fail("unable to create array");
if(!five)
if (!five)
fail("unable to create integer");
if(!seven)
if (!seven)
fail("unable to create integer");
if(!json_array_remove(array, 0))
if (!json_array_remove(array, 0))
fail("able to remove an unexisting index");
if(json_array_append(array, five))
if (json_array_append(array, five))
fail("unable to append");
if(!json_array_remove(array, 1))
if (!json_array_remove(array, 1))
fail("able to remove an unexisting index");
if(json_array_remove(array, 0))
if (json_array_remove(array, 0))
fail("unable to remove");
if(json_array_size(array) != 0)
if (json_array_size(array) != 0)
fail("array size is invalid after removing");
if(json_array_append(array, five) ||
json_array_append(array, seven) ||
json_array_append(array, five) ||
json_array_append(array, seven))
if (json_array_append(array, five) || json_array_append(array, seven) ||
json_array_append(array, five) || json_array_append(array, seven))
fail("unable to append");
if(json_array_remove(array, 2))
if (json_array_remove(array, 2))
fail("unable to remove");
if(json_array_size(array) != 3)
if (json_array_size(array) != 3)
fail("array size is invalid after removing");
if(json_array_get(array, 0) != five ||
json_array_get(array, 1) != seven ||
json_array_get(array, 2) != seven)
if (json_array_get(array, 0) != five || json_array_get(array, 1) != seven ||
json_array_get(array, 2) != seven)
fail("remove works incorrectly");
json_decref(array);
array = json_array();
for(i = 0; i < 4; i++) {
for (i = 0; i < 4; i++) {
json_array_append(array, five);
json_array_append(array, seven);
}
if(json_array_size(array) != 8)
if (json_array_size(array) != 8)
fail("unable to append 8 items to array");
/* Remove an element from a "full" array. */
@@ -272,8 +257,7 @@ static void test_remove(void)
json_decref(array);
}
static void test_clear(void)
{
static void test_clear(void) {
json_t *array, *five, *seven;
int i;
@@ -281,27 +265,27 @@ static void test_clear(void)
five = json_integer(5);
seven = json_integer(7);
if(!array)
if (!array)
fail("unable to create array");
if(!five || !seven)
if (!five || !seven)
fail("unable to create integer");
for(i = 0; i < 10; i++) {
if(json_array_append(array, five))
for (i = 0; i < 10; i++) {
if (json_array_append(array, five))
fail("unable to append");
}
for(i = 0; i < 10; i++) {
if(json_array_append(array, seven))
for (i = 0; i < 10; i++) {
if (json_array_append(array, seven))
fail("unable to append");
}
if(json_array_size(array) != 20)
if (json_array_size(array) != 20)
fail("array size is invalid after appending");
if(json_array_clear(array))
if (json_array_clear(array))
fail("unable to clear");
if(json_array_size(array) != 0)
if (json_array_size(array) != 0)
fail("array size is invalid after clearing");
json_decref(five);
@@ -309,8 +293,7 @@ static void test_clear(void)
json_decref(array);
}
static void test_extend(void)
{
static void test_extend(void) {
json_t *array1, *array2, *five, *seven;
int i;
@@ -319,32 +302,32 @@ static void test_extend(void)
five = json_integer(5);
seven = json_integer(7);
if(!array1 || !array2)
if (!array1 || !array2)
fail("unable to create array");
if(!five || !seven)
if (!five || !seven)
fail("unable to create integer");
for(i = 0; i < 10; i++) {
if(json_array_append(array1, five))
for (i = 0; i < 10; i++) {
if (json_array_append(array1, five))
fail("unable to append");
}
for(i = 0; i < 10; i++) {
if(json_array_append(array2, seven))
for (i = 0; i < 10; i++) {
if (json_array_append(array2, seven))
fail("unable to append");
}
if(json_array_size(array1) != 10 || json_array_size(array2) != 10)
if (json_array_size(array1) != 10 || json_array_size(array2) != 10)
fail("array size is invalid after appending");
if(json_array_extend(array1, array2))
if (json_array_extend(array1, array2))
fail("unable to extend");
for(i = 0; i < 10; i++) {
if(json_array_get(array1, i) != five)
for (i = 0; i < 10; i++) {
if (json_array_get(array1, i) != five)
fail("invalid array contents after extending");
}
for(i = 10; i < 20; i++) {
if(json_array_get(array1, i) != seven)
for (i = 10; i < 20; i++) {
if (json_array_get(array1, i) != seven)
fail("invalid array contents after extending");
}
@@ -354,44 +337,41 @@ static void test_extend(void)
json_decref(array2);
}
static void test_circular()
{
static void test_circular() {
json_t *array1, *array2;
/* the simple cases are checked */
array1 = json_array();
if(!array1)
if (!array1)
fail("unable to create array");
if(json_array_append(array1, array1) == 0)
if (json_array_append(array1, array1) == 0)
fail("able to append self");
if(json_array_insert(array1, 0, array1) == 0)
if (json_array_insert(array1, 0, array1) == 0)
fail("able to insert self");
if(json_array_append_new(array1, json_true()))
if (json_array_append_new(array1, json_true()))
fail("failed to append true");
if(json_array_set(array1, 0, array1) == 0)
if (json_array_set(array1, 0, array1) == 0)
fail("able to set self");
json_decref(array1);
/* create circular references */
array1 = json_array();
array2 = json_array();
if(!array1 || !array2)
if (!array1 || !array2)
fail("unable to create array");
if(json_array_append(array1, array2) ||
json_array_append(array2, array1))
if (json_array_append(array1, array2) || json_array_append(array2, array1))
fail("unable to append");
/* circularity is detected when dumping */
if(json_dumps(array1, 0) != NULL)
if (json_dumps(array1, 0) != NULL)
fail("able to dump circulars");
/* decref twice to deal with the circular references */
@@ -400,28 +380,99 @@ static void test_circular()
json_decref(array1);
}
static void test_array_foreach()
{
static void test_array_foreach() {
size_t index;
json_t *array1, *array2, *value;
array1 = json_pack("[sisisi]", "foo", 1, "bar", 2, "baz", 3);
array2 = json_array();
json_array_foreach(array1, index, value) {
json_array_append(array2, value);
}
if(!json_equal(array1, array2))
json_array_foreach(array1, index, value) { json_array_append(array2, value); }
if (!json_equal(array1, array2))
fail("json_array_foreach failed to iterate all elements");
json_decref(array1);
json_decref(array2);
}
static void test_bad_args(void) {
json_t *arr = json_array();
json_t *num = json_integer(1);
static void run_tests()
{
if (!arr || !num)
fail("failed to create required objects");
if (json_array_size(NULL) != 0)
fail("NULL array has nonzero size");
if (json_array_size(num) != 0)
fail("non-array has nonzero array size");
if (json_array_get(NULL, 0))
fail("json_array_get did not return NULL for non-array");
if (json_array_get(num, 0))
fail("json_array_get did not return NULL for non-array");
if (!json_array_set_new(NULL, 0, json_incref(num)))
fail("json_array_set_new did not return error for non-array");
if (!json_array_set_new(num, 0, json_incref(num)))
fail("json_array_set_new did not return error for non-array");
if (!json_array_set_new(arr, 0, NULL))
fail("json_array_set_new did not return error for NULL value");
if (!json_array_set_new(arr, 0, json_incref(arr)))
fail("json_array_set_new did not return error for value == array");
if (!json_array_remove(NULL, 0))
fail("json_array_remove did not return error for non-array");
if (!json_array_remove(num, 0))
fail("json_array_remove did not return error for non-array");
if (!json_array_clear(NULL))
fail("json_array_clear did not return error for non-array");
if (!json_array_clear(num))
fail("json_array_clear did not return error for non-array");
if (!json_array_append_new(NULL, json_incref(num)))
fail("json_array_append_new did not return error for non-array");
if (!json_array_append_new(num, json_incref(num)))
fail("json_array_append_new did not return error for non-array");
if (!json_array_append_new(arr, NULL))
fail("json_array_append_new did not return error for NULL value");
if (!json_array_append_new(arr, json_incref(arr)))
fail("json_array_append_new did not return error for value == array");
if (!json_array_insert_new(NULL, 0, json_incref(num)))
fail("json_array_insert_new did not return error for non-array");
if (!json_array_insert_new(num, 0, json_incref(num)))
fail("json_array_insert_new did not return error for non-array");
if (!json_array_insert_new(arr, 0, NULL))
fail("json_array_insert_new did not return error for NULL value");
if (!json_array_insert_new(arr, 0, json_incref(arr)))
fail("json_array_insert_new did not return error for value == array");
if (!json_array_extend(NULL, arr))
fail("json_array_extend did not return error for first argument "
"non-array");
if (!json_array_extend(num, arr))
fail("json_array_extend did not return error for first argument "
"non-array");
if (!json_array_extend(arr, NULL))
fail("json_array_extend did not return error for second argument "
"non-array");
if (!json_array_extend(arr, num))
fail("json_array_extend did not return error for second argument "
"non-array");
if (num->refcount != 1)
fail("unexpected reference count on num");
if (arr->refcount != 1)
fail("unexpected reference count on arr");
json_decref(num);
json_decref(arr);
}
static void run_tests() {
test_misc();
test_insert();
test_remove();
@@ -429,4 +480,5 @@ static void run_tests()
test_extend();
test_circular();
test_array_foreach();
test_bad_args();
}

View File

@@ -0,0 +1,168 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.h"
#include <jansson.h>
#include <stdio.h>
#include <string.h>
static int chaos_pos = 0;
static int chaos_fail = 0;
#define CHAOS_MAX_FAILURE 100
void *chaos_malloc(size_t size) {
if (chaos_pos == chaos_fail)
return NULL;
chaos_pos++;
return malloc(size);
}
void chaos_free(void *obj) { free(obj); }
/* Test all potential allocation failures. */
#define chaos_loop(condition, code, cleanup) \
{ \
chaos_pos = chaos_fail = 0; \
while (condition) { \
if (chaos_fail > CHAOS_MAX_FAILURE) \
fail("too many chaos failures"); \
code chaos_pos = 0; \
chaos_fail++; \
} \
cleanup \
}
#define chaos_loop_new_value(json, initcall) \
chaos_loop(!json, json = initcall;, json_decref(json); json = NULL;)
int test_unpack() {
int ret = -1;
int v1;
int v2;
json_error_t error;
json_t *root = json_pack("{s:i, s:i, s:i, s:i}", "n1", 1, "n2", 2, "n3", 3, "n4", 4);
if (!root)
return -1;
if (!json_unpack_ex(root, &error, JSON_STRICT, "{s:i, s:i}", "n1", &v1, "n2", &v2))
fail("Unexpected success");
if (json_error_code(&error) != json_error_end_of_input_expected) {
if (json_error_code(&error) != json_error_out_of_memory)
fail("Unexpected error code");
goto out;
}
if (strcmp(error.text, "2 object item(s) left unpacked: n3, n4"))
goto out;
ret = 0;
out:
json_decref(root);
return ret;
}
int dump_chaos_callback(const char *buffer, size_t size, void *data) {
json_t *obj = json_object();
(void)buffer;
(void)size;
(void)data;
if (!obj)
return -1;
json_decref(obj);
return 0;
}
static void test_chaos() {
json_malloc_t orig_malloc;
json_free_t orig_free;
json_t *json = NULL;
json_t *obj = json_object();
json_t *arr1 = json_array();
json_t *arr2 = json_array();
json_t *txt = json_string("test");
json_t *intnum = json_integer(1);
json_t *dblnum = json_real(0.5);
char *dumptxt = NULL;
json_t *dumpobj = json_pack("{s:[iiis], s:s}", "key1", 1, 2, 3, "txt", "key2", "v2");
int keyno;
if (!obj || !arr1 || !arr2 || !txt || !intnum || !dblnum || !dumpobj)
fail("failed to allocate basic objects");
json_get_alloc_funcs(&orig_malloc, &orig_free);
json_set_alloc_funcs(chaos_malloc, chaos_free);
chaos_loop_new_value(json, json_pack("{s:s}", "key", "value"));
chaos_loop_new_value(json, json_pack("{s:[]}", "key"));
chaos_loop_new_value(json, json_pack("[biIf]", 1, 1, (json_int_t)1, 1.0));
chaos_loop_new_value(json, json_pack("[s*,s*]", "v1", "v2"));
chaos_loop_new_value(json, json_pack("o", json_incref(txt)));
chaos_loop_new_value(json, json_pack("O", txt));
chaos_loop_new_value(json, json_pack("s++", "a", "long string to force realloc",
"another long string to force yet another "
"reallocation of the string because "
"that's what we are testing."));
chaos_loop(test_unpack(), , );
chaos_loop(json_dump_callback(dumpobj, dump_chaos_callback, NULL, JSON_INDENT(1)),
, );
chaos_loop(json_dump_callback(dumpobj, dump_chaos_callback, NULL,
JSON_INDENT(1) | JSON_SORT_KEYS),
, );
chaos_loop(!dumptxt, dumptxt = json_dumps(dumpobj, JSON_COMPACT);, free(dumptxt);
dumptxt = NULL;);
chaos_loop_new_value(json, json_copy(obj));
chaos_loop_new_value(json, json_deep_copy(obj));
chaos_loop_new_value(json, json_copy(arr1));
chaos_loop_new_value(json, json_deep_copy(arr1));
chaos_loop_new_value(json, json_copy(txt));
chaos_loop_new_value(json, json_copy(intnum));
chaos_loop_new_value(json, json_copy(dblnum));
#define JSON_LOAD_TXT "{\"n\":[1,2,3,4,5,6,7,8,9,10]}"
chaos_loop_new_value(json, json_loads(JSON_LOAD_TXT, 0, NULL));
chaos_loop_new_value(json, json_loadb(JSON_LOAD_TXT, strlen(JSON_LOAD_TXT), 0, NULL));
chaos_loop_new_value(json, json_sprintf("%s", "string"));
for (keyno = 0; keyno < 100; ++keyno) {
#if !defined(_MSC_VER) || _MSC_VER >= 1900
/* Skip this test on old Windows compilers. */
char testkey[10];
snprintf(testkey, sizeof(testkey), "test%d", keyno);
chaos_loop(json_object_set_new_nocheck(obj, testkey, json_object()), , );
#endif
chaos_loop(json_array_append_new(arr1, json_null()), , );
chaos_loop(json_array_insert_new(arr2, 0, json_null()), , );
}
chaos_loop(json_array_extend(arr1, arr2), , );
chaos_loop(json_string_set_nocheck(txt, "test"), , );
json_set_alloc_funcs(orig_malloc, orig_free);
json_decref(obj);
json_decref(arr1);
json_decref(arr2);
json_decref(txt);
json_decref(intnum);
json_decref(dblnum);
json_decref(dumpobj);
}
static void run_tests() { test_chaos(); }

View File

@@ -1,25 +1,24 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include <jansson.h>
#include "util.h"
#include <jansson.h>
#include <string.h>
static void test_copy_simple(void)
{
static void test_copy_simple(void) {
json_t *value, *copy;
if(json_copy(NULL))
if (json_copy(NULL))
fail("copying NULL doesn't return NULL");
/* true */
value = json_true();
copy = json_copy(value);
if(value != copy)
if (value != copy)
fail("copying true failed");
json_decref(value);
json_decref(copy);
@@ -27,7 +26,7 @@ static void test_copy_simple(void)
/* false */
value = json_false();
copy = json_copy(value);
if(value != copy)
if (value != copy)
fail("copying false failed");
json_decref(value);
json_decref(copy);
@@ -35,71 +34,70 @@ static void test_copy_simple(void)
/* null */
value = json_null();
copy = json_copy(value);
if(value != copy)
if (value != copy)
fail("copying null failed");
json_decref(value);
json_decref(copy);
/* string */
value = json_string("foo");
if(!value)
if (!value)
fail("unable to create a string");
copy = json_copy(value);
if(!copy)
if (!copy)
fail("unable to copy a string");
if(copy == value)
if (copy == value)
fail("copying a string doesn't copy");
if(!json_equal(copy, value))
if (!json_equal(copy, value))
fail("copying a string produces an inequal copy");
if(value->refcount != 1 || copy->refcount != 1)
if (value->refcount != 1 || copy->refcount != 1)
fail("invalid refcounts");
json_decref(value);
json_decref(copy);
/* integer */
value = json_integer(543);
if(!value)
if (!value)
fail("unable to create an integer");
copy = json_copy(value);
if(!copy)
if (!copy)
fail("unable to copy an integer");
if(copy == value)
if (copy == value)
fail("copying an integer doesn't copy");
if(!json_equal(copy, value))
if (!json_equal(copy, value))
fail("copying an integer produces an inequal copy");
if(value->refcount != 1 || copy->refcount != 1)
if (value->refcount != 1 || copy->refcount != 1)
fail("invalid refcounts");
json_decref(value);
json_decref(copy);
/* real */
value = json_real(123e9);
if(!value)
if (!value)
fail("unable to create a real");
copy = json_copy(value);
if(!copy)
if (!copy)
fail("unable to copy a real");
if(copy == value)
if (copy == value)
fail("copying a real doesn't copy");
if(!json_equal(copy, value))
if (!json_equal(copy, value))
fail("copying a real produces an inequal copy");
if(value->refcount != 1 || copy->refcount != 1)
if (value->refcount != 1 || copy->refcount != 1)
fail("invalid refcounts");
json_decref(value);
json_decref(copy);
}
static void test_deep_copy_simple(void)
{
static void test_deep_copy_simple(void) {
json_t *value, *copy;
if(json_deep_copy(NULL))
if (json_deep_copy(NULL))
fail("deep copying NULL doesn't return NULL");
/* true */
value = json_true();
copy = json_deep_copy(value);
if(value != copy)
if (value != copy)
fail("deep copying true failed");
json_decref(value);
json_decref(copy);
@@ -107,7 +105,7 @@ static void test_deep_copy_simple(void)
/* false */
value = json_false();
copy = json_deep_copy(value);
if(value != copy)
if (value != copy)
fail("deep copying false failed");
json_decref(value);
json_decref(copy);
@@ -115,82 +113,80 @@ static void test_deep_copy_simple(void)
/* null */
value = json_null();
copy = json_deep_copy(value);
if(value != copy)
if (value != copy)
fail("deep copying null failed");
json_decref(value);
json_decref(copy);
/* string */
value = json_string("foo");
if(!value)
if (!value)
fail("unable to create a string");
copy = json_deep_copy(value);
if(!copy)
if (!copy)
fail("unable to deep copy a string");
if(copy == value)
if (copy == value)
fail("deep copying a string doesn't copy");
if(!json_equal(copy, value))
if (!json_equal(copy, value))
fail("deep copying a string produces an inequal copy");
if(value->refcount != 1 || copy->refcount != 1)
if (value->refcount != 1 || copy->refcount != 1)
fail("invalid refcounts");
json_decref(value);
json_decref(copy);
/* integer */
value = json_integer(543);
if(!value)
if (!value)
fail("unable to create an integer");
copy = json_deep_copy(value);
if(!copy)
if (!copy)
fail("unable to deep copy an integer");
if(copy == value)
if (copy == value)
fail("deep copying an integer doesn't copy");
if(!json_equal(copy, value))
if (!json_equal(copy, value))
fail("deep copying an integer produces an inequal copy");
if(value->refcount != 1 || copy->refcount != 1)
if (value->refcount != 1 || copy->refcount != 1)
fail("invalid refcounts");
json_decref(value);
json_decref(copy);
/* real */
value = json_real(123e9);
if(!value)
if (!value)
fail("unable to create a real");
copy = json_deep_copy(value);
if(!copy)
if (!copy)
fail("unable to deep copy a real");
if(copy == value)
if (copy == value)
fail("deep copying a real doesn't copy");
if(!json_equal(copy, value))
if (!json_equal(copy, value))
fail("deep copying a real produces an inequal copy");
if(value->refcount != 1 || copy->refcount != 1)
if (value->refcount != 1 || copy->refcount != 1)
fail("invalid refcounts");
json_decref(value);
json_decref(copy);
}
static void test_copy_array(void)
{
static void test_copy_array(void) {
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
json_t *array, *copy;
size_t i;
array = json_loads(json_array_text, 0, NULL);
if(!array)
if (!array)
fail("unable to parse an array");
copy = json_copy(array);
if(!copy)
if (!copy)
fail("unable to copy an array");
if(copy == array)
if (copy == array)
fail("copying an array doesn't copy");
if(!json_equal(copy, array))
if (!json_equal(copy, array))
fail("copying an array produces an inequal copy");
for(i = 0; i < json_array_size(copy); i++)
{
if(json_array_get(array, i) != json_array_get(copy, i))
for (i = 0; i < json_array_size(copy); i++) {
if (json_array_get(array, i) != json_array_get(copy, i))
fail("copying an array modifies its elements");
}
@@ -198,28 +194,26 @@ static void test_copy_array(void)
json_decref(copy);
}
static void test_deep_copy_array(void)
{
static void test_deep_copy_array(void) {
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
json_t *array, *copy;
size_t i;
array = json_loads(json_array_text, 0, NULL);
if(!array)
if (!array)
fail("unable to parse an array");
copy = json_deep_copy(array);
if(!copy)
if (!copy)
fail("unable to deep copy an array");
if(copy == array)
if (copy == array)
fail("deep copying an array doesn't copy");
if(!json_equal(copy, array))
if (!json_equal(copy, array))
fail("deep copying an array produces an inequal copy");
for(i = 0; i < json_array_size(copy); i++)
{
if(json_array_get(array, i) == json_array_get(copy, i))
for (i = 0; i < json_array_size(copy); i++) {
if (json_array_get(array, i) == json_array_get(copy, i))
fail("deep copying an array doesn't copy its elements");
}
@@ -227,29 +221,31 @@ static void test_deep_copy_array(void)
json_decref(copy);
}
static void test_copy_object(void)
{
static void test_copy_object(void) {
const char *json_object_text =
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
const char *keys[] = {"foo", "a", "b", "c"};
int i;
json_t *object, *copy;
void *iter;
object = json_loads(json_object_text, 0, NULL);
if(!object)
if (!object)
fail("unable to parse an object");
copy = json_copy(object);
if(!copy)
if (!copy)
fail("unable to copy an object");
if(copy == object)
if (copy == object)
fail("copying an object doesn't copy");
if(!json_equal(copy, object))
if (!json_equal(copy, object))
fail("copying an object produces an inequal copy");
i = 0;
iter = json_object_iter(object);
while(iter)
{
while (iter) {
const char *key;
json_t *value1, *value2;
@@ -257,39 +253,45 @@ static void test_copy_object(void)
value1 = json_object_iter_value(iter);
value2 = json_object_get(copy, key);
if(value1 != value2)
fail("deep copying an object modifies its items");
if (value1 != value2)
fail("copying an object modifies its items");
if (strcmp(key, keys[i]) != 0)
fail("copying an object doesn't preserve key order");
iter = json_object_iter_next(object, iter);
i++;
}
json_decref(object);
json_decref(copy);
}
static void test_deep_copy_object(void)
{
static void test_deep_copy_object(void) {
const char *json_object_text =
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
const char *keys[] = {"foo", "a", "b", "c"};
int i;
json_t *object, *copy;
void *iter;
object = json_loads(json_object_text, 0, NULL);
if(!object)
if (!object)
fail("unable to parse an object");
copy = json_deep_copy(object);
if(!copy)
if (!copy)
fail("unable to deep copy an object");
if(copy == object)
if (copy == object)
fail("deep copying an object doesn't copy");
if(!json_equal(copy, object))
if (!json_equal(copy, object))
fail("deep copying an object produces an inequal copy");
i = 0;
iter = json_object_iter(object);
while(iter)
{
while (iter) {
const char *key;
json_t *value1, *value2;
@@ -297,22 +299,77 @@ static void test_deep_copy_object(void)
value1 = json_object_iter_value(iter);
value2 = json_object_get(copy, key);
if(value1 == value2)
if (value1 == value2)
fail("deep copying an object doesn't copy its items");
if (strcmp(key, keys[i]) != 0)
fail("deep copying an object doesn't preserve key order");
iter = json_object_iter_next(object, iter);
i++;
}
json_decref(object);
json_decref(copy);
}
static void run_tests()
{
static void test_deep_copy_circular_references(void) {
/* Construct a JSON object/array with a circular reference:
object: {"a": {"b": {"c": <circular reference to $.a>}}}
array: [[[<circular reference to the $[0] array>]]]
Deep copy it, remove the circular reference and deep copy again.
*/
json_t *json;
json_t *copy;
json = json_object();
json_object_set_new(json, "a", json_object());
json_object_set_new(json_object_get(json, "a"), "b", json_object());
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
json_object_get(json, "a"));
copy = json_deep_copy(json);
if (copy)
fail("json_deep_copy copied a circular reference!");
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
copy = json_deep_copy(json);
if (!copy)
fail("json_deep_copy failed!");
json_decref(copy);
json_decref(json);
json = json_array();
json_array_append_new(json, json_array());
json_array_append_new(json_array_get(json, 0), json_array());
json_array_append(json_array_get(json_array_get(json, 0), 0),
json_array_get(json, 0));
copy = json_deep_copy(json);
if (copy)
fail("json_deep_copy copied a circular reference!");
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
copy = json_deep_copy(json);
if (!copy)
fail("json_deep_copy failed!");
json_decref(copy);
json_decref(json);
}
static void run_tests() {
test_copy_simple();
test_deep_copy_simple();
test_copy_array();
test_deep_copy_array();
test_copy_object();
test_deep_copy_object();
test_deep_copy_circular_references();
}

View File

@@ -1,39 +1,52 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "jansson_private_config.h"
#include <jansson.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "util.h"
#ifdef __MINGW32__
#include <fcntl.h>
#define pipe(fds) _pipe(fds, 1024, _O_BINARY)
#endif
static int encode_null_callback(const char *buffer, size_t size, void *data)
{
static int encode_null_callback(const char *buffer, size_t size, void *data) {
(void)buffer;
(void)size;
(void)data;
return 0;
}
static void encode_null()
{
if(json_dumps(NULL, JSON_ENCODE_ANY) != NULL)
static void encode_null() {
if (json_dumps(NULL, JSON_ENCODE_ANY) != NULL)
fail("json_dumps didn't fail for NULL");
if(json_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1)
if (json_dumpb(NULL, NULL, 0, JSON_ENCODE_ANY) != 0)
fail("json_dumpb didn't fail for NULL");
if (json_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1)
fail("json_dumpf didn't fail for NULL");
#ifdef HAVE_UNISTD_H
if (json_dumpfd(NULL, STDERR_FILENO, JSON_ENCODE_ANY) != -1)
fail("json_dumpfd didn't fail for NULL");
#endif
/* Don't test json_dump_file to avoid creating a file */
if(json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1)
if (json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1)
fail("json_dump_callback didn't fail for NULL");
}
static void encode_twice()
{
static void encode_twice() {
/* Encode an empty object/array, add an item, encode again */
json_t *json;
@@ -41,35 +54,34 @@ static void encode_twice()
json = json_object();
result = json_dumps(json, 0);
if(!result || strcmp(result, "{}"))
fail("json_dumps failed");
if (!result || strcmp(result, "{}"))
fail("json_dumps failed");
free(result);
json_object_set_new(json, "foo", json_integer(5));
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"foo\": 5}"))
fail("json_dumps failed");
if (!result || strcmp(result, "{\"foo\": 5}"))
fail("json_dumps failed");
free(result);
json_decref(json);
json = json_array();
result = json_dumps(json, 0);
if(!result || strcmp(result, "[]"))
fail("json_dumps failed");
if (!result || strcmp(result, "[]"))
fail("json_dumps failed");
free(result);
json_array_append_new(json, json_integer(5));
result = json_dumps(json, 0);
if(!result || strcmp(result, "[5]"))
fail("json_dumps failed");
if (!result || strcmp(result, "[5]"))
fail("json_dumps failed");
free(result);
json_decref(json);
}
static void circular_references()
{
static void circular_references() {
/* Construct a JSON object/array with a circular reference:
object: {"a": {"b": {"c": <circular reference to $.a>}}}
@@ -87,13 +99,13 @@ static void circular_references()
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
json_object_get(json, "a"));
if(json_dumps(json, 0))
if (json_dumps(json, 0))
fail("json_dumps encoded a circular reference!");
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
if (!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
fail("json_dumps failed!");
free(result);
@@ -105,59 +117,58 @@ static void circular_references()
json_array_append(json_array_get(json_array_get(json, 0), 0),
json_array_get(json, 0));
if(json_dumps(json, 0))
if (json_dumps(json, 0))
fail("json_dumps encoded a circular reference!");
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
result = json_dumps(json, 0);
if(!result || strcmp(result, "[[[]]]"))
if (!result || strcmp(result, "[[[]]]"))
fail("json_dumps failed!");
free(result);
json_decref(json);
}
static void encode_other_than_array_or_object()
{
static void encode_other_than_array_or_object() {
/* Encoding anything other than array or object should only
* succeed if the JSON_ENCODE_ANY flag is used */
json_t *json;
FILE *fp = NULL;
char *result;
json = json_string("foo");
if(json_dumps(json, 0) != NULL)
if (json_dumps(json, 0) != NULL)
fail("json_dumps encoded a string!");
if(json_dumpf(json, fp, 0) == 0)
if (json_dumpf(json, NULL, 0) == 0)
fail("json_dumpf encoded a string!");
if (json_dumpfd(json, -1, 0) == 0)
fail("json_dumpfd encoded a string!");
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || strcmp(result, "\"foo\"") != 0)
if (!result || strcmp(result, "\"foo\"") != 0)
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
free(result);
json_decref(json);
json = json_integer(42);
if(json_dumps(json, 0) != NULL)
if (json_dumps(json, 0) != NULL)
fail("json_dumps encoded an integer!");
if(json_dumpf(json, fp, 0) == 0)
if (json_dumpf(json, NULL, 0) == 0)
fail("json_dumpf encoded an integer!");
if (json_dumpfd(json, -1, 0) == 0)
fail("json_dumpfd encoded an integer!");
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || strcmp(result, "42") != 0)
if (!result || strcmp(result, "42") != 0)
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
free(result);
json_decref(json);
}
static void escape_slashes()
{
static void escape_slashes() {
/* Test dump escaping slashes */
json_t *json;
@@ -167,39 +178,134 @@ static void escape_slashes()
json_object_set_new(json, "url", json_string("https://github.com/akheron/jansson"));
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"url\": \"https://github.com/akheron/jansson\"}"))
if (!result || strcmp(result, "{\"url\": \"https://github.com/akheron/jansson\"}"))
fail("json_dumps failed to not escape slashes");
free(result);
result = json_dumps(json, JSON_ESCAPE_SLASH);
if(!result || strcmp(result, "{\"url\": \"https:\\/\\/github.com\\/akheron\\/jansson\"}"))
if (!result ||
strcmp(result, "{\"url\": \"https:\\/\\/github.com\\/akheron\\/jansson\"}"))
fail("json_dumps failed to escape slashes");
free(result);
json_decref(json);
}
static void encode_nul_byte()
{
static void encode_nul_byte() {
json_t *json;
char *result;
json = json_stringn("nul byte \0 in string", 20);
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || memcmp(result, "\"nul byte \\u0000 in string\"", 27))
if (!result || memcmp(result, "\"nul byte \\u0000 in string\"", 27))
fail("json_dumps failed to dump an embedded NUL byte");
free(result);
json_decref(json);
}
static void run_tests()
{
static void dump_file() {
json_t *json;
int result;
result = json_dump_file(NULL, "", 0);
if (result != -1)
fail("json_dump_file succeeded with invalid args");
json = json_object();
result = json_dump_file(json, "json_dump_file.json", 0);
if (result != 0)
fail("json_dump_file failed");
json_decref(json);
remove("json_dump_file.json");
}
static void dumpb() {
char buf[2];
json_t *obj;
size_t size;
obj = json_object();
size = json_dumpb(obj, buf, sizeof(buf), 0);
if (size != 2 || strncmp(buf, "{}", 2))
fail("json_dumpb failed");
json_decref(obj);
obj = json_pack("{s:s}", "foo", "bar");
size = json_dumpb(obj, buf, sizeof(buf), JSON_COMPACT);
if (size != 13)
fail("json_dumpb size check failed");
json_decref(obj);
}
static void dumpfd() {
#ifdef HAVE_UNISTD_H
int fds[2] = {-1, -1};
json_t *a, *b;
if (pipe(fds))
fail("pipe() failed");
a = json_pack("{s:s}", "foo", "bar");
if (json_dumpfd(a, fds[1], 0))
fail("json_dumpfd() failed");
close(fds[1]);
b = json_loadfd(fds[0], 0, NULL);
if (!b)
fail("json_loadfd() failed");
close(fds[0]);
if (!json_equal(a, b))
fail("json_equal() failed for fd test");
json_decref(a);
json_decref(b);
#endif
}
static void embed() {
static const char *plains[] = {"{\"bar\":[],\"foo\":{}}", "[[],{}]", "{}", "[]",
NULL};
size_t i;
for (i = 0; plains[i]; i++) {
const char *plain = plains[i];
json_t *parse = NULL;
char *embed = NULL;
size_t psize = 0;
size_t esize = 0;
psize = strlen(plain) - 2;
embed = calloc(1, psize);
parse = json_loads(plain, 0, NULL);
esize =
json_dumpb(parse, embed, psize, JSON_COMPACT | JSON_SORT_KEYS | JSON_EMBED);
json_decref(parse);
if (esize != psize)
fail("json_dumpb(JSON_EMBED) returned an invalid size");
if (strncmp(plain + 1, embed, esize) != 0)
fail("json_dumps(JSON_EMBED) returned an invalid value");
free(embed);
}
}
static void run_tests() {
encode_null();
encode_twice();
circular_references();
encode_other_than_array_or_object();
escape_slashes();
encode_nul_byte();
dump_file();
dumpb();
dumpfd();
embed();
}

View File

@@ -1,14 +1,14 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"
#include <jansson.h>
#include <stdlib.h>
#include <string.h>
struct my_sink {
char *buf;
@@ -26,15 +26,14 @@ static int my_writer(const char *buffer, size_t len, void *data) {
return 0;
}
static void run_tests()
{
static void run_tests() {
struct my_sink s;
json_t *json;
const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
char *dumped_to_string;
json = json_loads(str, 0, NULL);
if(!json) {
if (!json) {
fail("json_loads failed");
}
@@ -64,7 +63,8 @@ static void run_tests()
json_decref(json);
free(dumped_to_string);
free(s.buf);
fail("json_dump_callback and json_dumps did not produce identical output");
fail("json_dump_callback and json_dumps did not produce identical "
"output");
}
s.off = 1;
@@ -72,7 +72,8 @@ static void run_tests()
json_decref(json);
free(dumped_to_string);
free(s.buf);
fail("json_dump_callback succeeded on a short buffer when it should have failed");
fail("json_dump_callback succeeded on a short buffer when it should "
"have failed");
}
json_decref(json);

View File

@@ -1,42 +1,41 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include "util.h"
#include <jansson.h>
static void test_equal_simple()
{
static void test_equal_simple() {
json_t *value1, *value2;
if(json_equal(NULL, NULL))
if (json_equal(NULL, NULL))
fail("json_equal fails for two NULLs");
value1 = json_true();
if(json_equal(value1, NULL) || json_equal(NULL, value1))
if (json_equal(value1, NULL) || json_equal(NULL, value1))
fail("json_equal fails for NULL");
/* this covers true, false and null as they are singletons */
if(!json_equal(value1, value1))
if (!json_equal(value1, value1))
fail("identical objects are not equal");
json_decref(value1);
/* integer */
value1 = json_integer(1);
value2 = json_integer(1);
if(!value1 || !value2)
if (!value1 || !value2)
fail("unable to create integers");
if(!json_equal(value1, value2))
if (!json_equal(value1, value2))
fail("json_equal fails for two equal integers");
json_decref(value2);
value2 = json_integer(2);
if(!value2)
if (!value2)
fail("unable to create an integer");
if(json_equal(value1, value2))
if (json_equal(value1, value2))
fail("json_equal fails for two inequal integers");
json_decref(value1);
@@ -45,16 +44,16 @@ static void test_equal_simple()
/* real */
value1 = json_real(1.2);
value2 = json_real(1.2);
if(!value1 || !value2)
if (!value1 || !value2)
fail("unable to create reals");
if(!json_equal(value1, value2))
if (!json_equal(value1, value2))
fail("json_equal fails for two equal reals");
json_decref(value2);
value2 = json_real(3.141592);
if(!value2)
if (!value2)
fail("unable to create an real");
if(json_equal(value1, value2))
if (json_equal(value1, value2))
fail("json_equal fails for two inequal reals");
json_decref(value1);
@@ -63,32 +62,38 @@ static void test_equal_simple()
/* string */
value1 = json_string("foo");
value2 = json_string("foo");
if(!value1 || !value2)
if (!value1 || !value2)
fail("unable to create strings");
if(!json_equal(value1, value2))
if (!json_equal(value1, value2))
fail("json_equal fails for two equal strings");
json_decref(value2);
value2 = json_string("bar");
if(!value2)
if (!value2)
fail("unable to create an string");
if(json_equal(value1, value2))
if (json_equal(value1, value2))
fail("json_equal fails for two inequal strings");
json_decref(value2);
value2 = json_string("bar2");
if (!value2)
fail("unable to create an string");
if (json_equal(value1, value2))
fail("json_equal fails for two inequal length strings");
json_decref(value1);
json_decref(value2);
}
static void test_equal_array()
{
static void test_equal_array() {
json_t *array1, *array2;
array1 = json_array();
array2 = json_array();
if(!array1 || !array2)
if (!array1 || !array2)
fail("unable to create arrays");
if(!json_equal(array1, array2))
if (!json_equal(array1, array2))
fail("json_equal fails for two empty arrays");
json_array_append_new(array1, json_integer(1));
@@ -97,31 +102,30 @@ static void test_equal_array()
json_array_append_new(array2, json_string("foo"));
json_array_append_new(array1, json_integer(2));
json_array_append_new(array2, json_integer(2));
if(!json_equal(array1, array2))
if (!json_equal(array1, array2))
fail("json_equal fails for two equal arrays");
json_array_remove(array2, 2);
if(json_equal(array1, array2))
if (json_equal(array1, array2))
fail("json_equal fails for two inequal arrays");
json_array_append_new(array2, json_integer(3));
if(json_equal(array1, array2))
if (json_equal(array1, array2))
fail("json_equal fails for two inequal arrays");
json_decref(array1);
json_decref(array2);
}
static void test_equal_object()
{
static void test_equal_object() {
json_t *object1, *object2;
object1 = json_object();
object2 = json_object();
if(!object1 || !object2)
if (!object1 || !object2)
fail("unable to create objects");
if(!json_equal(object1, object2))
if (!json_equal(object1, object2))
fail("json_equal fails for two empty objects");
json_object_set_new(object1, "a", json_integer(1));
@@ -130,58 +134,67 @@ static void test_equal_object()
json_object_set_new(object2, "b", json_string("foo"));
json_object_set_new(object1, "c", json_integer(2));
json_object_set_new(object2, "c", json_integer(2));
if(!json_equal(object1, object2))
if (!json_equal(object1, object2))
fail("json_equal fails for two equal objects");
json_object_del(object2, "c");
if(json_equal(object1, object2))
if (json_equal(object1, object2))
fail("json_equal fails for two inequal objects");
json_object_set_new(object2, "c", json_integer(3));
if(json_equal(object1, object2))
if (json_equal(object1, object2))
fail("json_equal fails for two inequal objects");
json_object_del(object2, "c");
json_object_set_new(object2, "d", json_integer(2));
if(json_equal(object1, object2))
if (json_equal(object1, object2))
fail("json_equal fails for two inequal objects");
json_decref(object1);
json_decref(object2);
}
static void test_equal_complex()
{
json_t *value1, *value2;
static void test_equal_complex() {
json_t *value1, *value2, *value3;
const char *complex_json =
"{"
" \"integer\": 1, "
" \"real\": 3.141592, "
" \"string\": \"foobar\", "
" \"true\": true, "
" \"object\": {"
" \"array-in-object\": [1,true,\"foo\",{}],"
" \"object-in-object\": {\"foo\": \"bar\"}"
" },"
" \"array\": [\"foo\", false, null, 1.234]"
"}";
const char *complex_json = "{"
" \"integer\": 1, "
" \"real\": 3.141592, "
" \"string\": \"foobar\", "
" \"true\": true, "
" \"object\": {"
" \"array-in-object\": [1,true,\"foo\",{}],"
" \"object-in-object\": {\"foo\": \"bar\"}"
" },"
" \"array\": [\"foo\", false, null, 1.234]"
"}";
value1 = json_loads(complex_json, 0, NULL);
value2 = json_loads(complex_json, 0, NULL);
if(!value1 || !value2)
value3 = json_loads(complex_json, 0, NULL);
if (!value1 || !value2)
fail("unable to parse JSON");
if(!json_equal(value1, value2))
fail("json_equal fails for two inequal strings");
if (!json_equal(value1, value2))
fail("json_equal fails for two equal objects");
json_array_set_new(
json_object_get(json_object_get(value2, "object"), "array-in-object"), 1,
json_false());
if (json_equal(value1, value2))
fail("json_equal fails for two inequal objects");
json_object_set_new(
json_object_get(json_object_get(value3, "object"), "object-in-object"), "foo",
json_string("baz"));
if (json_equal(value1, value3))
fail("json_equal fails for two inequal objects");
json_decref(value1);
json_decref(value2);
/* TODO: There's no negative test case here */
json_decref(value3);
}
static void run_tests()
{
static void run_tests() {
test_equal_simple();
test_equal_array();
test_equal_object();

View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) 2020 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "util.h"
#include <jansson.h>
#include <string.h>
static void test_keylen_iterator(json_t *object) {
const char key1[] = {'t', 'e', 's', 't', '1'};
const char key2[] = {'t', 'e', 's', 't'};
const char key3[] = {'t', 'e', 's', '\0', 't'};
const char key4[] = {'t', 'e', 's', 't', '\0'};
const char *reference_keys[] = {key1, key2, key3, key4};
const size_t reference_keys_len[] = {sizeof(key1), sizeof(key2), sizeof(key3),
sizeof(key4)};
size_t index = 0;
json_t *value;
const char *key;
size_t keylen;
json_object_keylen_foreach(object, key, keylen, value) {
if (keylen != reference_keys_len[index])
fail("invalid key len in iterator");
if (memcmp(key, reference_keys[index], reference_keys_len[index]) != 0)
fail("invalid key in iterator");
index++;
}
}
static void test_keylen(void) {
json_t *obj = json_object();
const char key[] = {'t', 'e', 's', 't', '1'};
const char key2[] = {'t', 'e', 's', 't'};
const char key3[] = {'t', 'e', 's', '\0', 't'};
const char key4[] = {'t', 'e', 's', 't', '\0'};
if (json_object_size(obj) != 0)
fail("incorrect json");
json_object_set_new_nocheck(obj, "test1", json_true());
if (json_object_size(obj) != 1)
fail("incorrect json");
if (json_object_getn(obj, key, sizeof(key)) != json_true())
fail("json_object_getn failed");
if (json_object_getn(obj, key2, sizeof(key2)) != NULL)
fail("false positive json_object_getn by key2");
if (json_object_setn_nocheck(obj, key2, sizeof(key2), json_false()))
fail("json_object_setn_nocheck for key2 failed");
if (json_object_size(obj) != 2)
fail("incorrect json");
if (json_object_get(obj, "test") != json_false())
fail("json_object_setn_nocheck for key2 failed");
if (json_object_getn(obj, key2, sizeof(key2)) != json_false())
fail("json_object_getn by key 2 failed");
if (json_object_getn(obj, key3, sizeof(key3)) != NULL)
fail("false positive json_object_getn by key3");
if (json_object_setn_nocheck(obj, key3, sizeof(key3), json_false()))
fail("json_object_setn_nocheck for key3 failed");
if (json_object_size(obj) != 3)
fail("incorrect json");
if (json_object_getn(obj, key3, sizeof(key3)) != json_false())
fail("json_object_getn by key 3 failed");
if (json_object_getn(obj, key4, sizeof(key4)) != NULL)
fail("false positive json_object_getn by key3");
if (json_object_setn_nocheck(obj, key4, sizeof(key4), json_false()))
fail("json_object_setn_nocheck for key3 failed");
if (json_object_size(obj) != 4)
fail("incorrect json");
test_keylen_iterator(obj);
if (json_object_getn(obj, key4, sizeof(key4)) != json_false())
fail("json_object_getn by key 3 failed");
if (json_object_size(obj) != 4)
fail("incorrect json");
if (json_object_deln(obj, key4, sizeof(key4)))
fail("json_object_deln failed");
if (json_object_getn(obj, key4, sizeof(key4)) != NULL)
fail("json_object_deln failed");
if (json_object_size(obj) != 3)
fail("incorrect json");
if (json_object_deln(obj, key3, sizeof(key3)))
fail("json_object_deln failed");
if (json_object_getn(obj, key3, sizeof(key3)) != NULL)
fail("json_object_deln failed");
if (json_object_size(obj) != 2)
fail("incorrect json");
if (json_object_deln(obj, key2, sizeof(key2)))
fail("json_object_deln failed");
if (json_object_getn(obj, key2, sizeof(key2)) != NULL)
fail("json_object_deln failed");
if (json_object_size(obj) != 1)
fail("incorrect json");
if (json_object_deln(obj, key, sizeof(key)))
fail("json_object_deln failed");
if (json_object_getn(obj, key, sizeof(key)) != NULL)
fail("json_object_deln failed");
if (json_object_size(obj) != 0)
fail("incorrect json");
json_decref(obj);
}
static void test_invalid_keylen(void) {
json_t *obj = json_object();
json_t *empty_obj = json_object();
const char key[] = {'t', 'e', 's', 't', '1'};
json_object_set_new_nocheck(obj, "test1", json_true());
if (json_object_getn(NULL, key, sizeof(key)) != NULL)
fail("json_object_getn on NULL failed");
if (json_object_getn(obj, NULL, sizeof(key)) != NULL)
fail("json_object_getn on NULL failed");
if (json_object_getn(obj, key, 0) != NULL)
fail("json_object_getn on NULL failed");
if (!json_object_setn_new(obj, NULL, sizeof(key), json_true()))
fail("json_object_setn_new with NULL key failed");
if (!json_object_setn_new_nocheck(obj, NULL, sizeof(key), json_true()))
fail("json_object_setn_new_nocheck with NULL key failed");
if (!json_object_del(obj, NULL))
fail("json_object_del with NULL failed");
if (!json_object_deln(empty_obj, key, sizeof(key)))
fail("json_object_deln with empty object failed");
if (!json_object_deln(obj, key, sizeof(key) - 1))
fail("json_object_deln with incomplete key failed");
json_decref(obj);
json_decref(empty_obj);
}
static void test_binary_keys(void) {
json_t *obj = json_object();
int key1 = 0;
int key2 = 1;
json_object_setn_nocheck(obj, (const char *)&key1, sizeof(key1), json_true());
json_object_setn_nocheck(obj, (const char *)&key2, sizeof(key2), json_true());
if (!json_is_true(json_object_getn(obj, (const char *)&key1, sizeof(key1))))
fail("cannot get integer key1");
if (!json_is_true(json_object_getn(obj, (const char *)&key1, sizeof(key2))))
fail("cannot get integer key2");
if (json_object_size(obj) != 2)
fail("binary object size missmatch");
if (json_object_deln(obj, (const char *)&key1, sizeof(key1)))
fail("cannot del integer key1");
if (json_object_size(obj) != 1)
fail("binary object size missmatch");
if (json_object_deln(obj, (const char *)&key2, sizeof(key2)))
fail("cannot del integer key2");
if (json_object_size(obj) != 0)
fail("binary object size missmatch");
json_decref(obj);
}
static void test_dump_order(void) {
json_t *obj = json_object();
char key1[] = {'k', '\0', '-', '2'};
char key2[] = {'k', '\0', '-', '1'};
const char expected_sorted_str[] =
"{\"k\\u0000-1\": \"first\", \"k\\u0000-2\": \"second\"}";
const char expected_nonsorted_str[] =
"{\"k\\u0000-2\": \"second\", \"k\\u0000-1\": \"first\"}";
char *out;
json_object_setn_new_nocheck(obj, key1, sizeof(key1), json_string("second"));
json_object_setn_new_nocheck(obj, key2, sizeof(key2), json_string("first"));
out = malloc(512);
json_dumpb(obj, out, 512, 0);
if (memcmp(expected_nonsorted_str, out, sizeof(expected_nonsorted_str) - 1) != 0)
fail("preserve order failed");
json_dumpb(obj, out, 512, JSON_SORT_KEYS);
if (memcmp(expected_sorted_str, out, sizeof(expected_sorted_str) - 1) != 0)
fail("utf-8 sort failed");
free(out);
json_decref(obj);
}
static void run_tests() {
test_keylen();
test_invalid_keylen();
test_binary_keys();
test_dump_order();
}

View File

@@ -1,68 +1,86 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "util.h"
#include <jansson.h>
#include <string.h>
#include "util.h"
static void file_not_found()
{
static void file_not_found() {
json_t *json;
json_error_t error;
char *pos;
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
if(json)
if (json)
fail("json_load_file returned non-NULL for a nonexistent file");
if(error.line != -1)
if (error.line != -1)
fail("json_load_file returned an invalid line number");
/* The error message is locale specific, only check the beginning
of the error message. */
pos = strchr(error.text, ':');
if(!pos)
if (!pos)
fail("json_load_file returne an invalid error message");
*pos = '\0';
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0)
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 reject_duplicates()
{
static void very_long_file_name() {
json_t *json;
json_error_t error;
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);
json = json_load_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
0, &error);
if (json)
fail("json_load_file returned non-NULL for a nonexistent file");
if (error.line != -1)
fail("json_load_file returned an invalid line number");
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 disable_eof_check()
{
static void reject_duplicates() {
json_error_t error;
if (json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
fail("json_loads did not detect a duplicate key");
check_error(json_error_duplicate_key, "duplicate object key near '\"foo\"'",
"<string>", 1, 16, 16);
}
static void disable_eof_check() {
json_error_t error;
json_t *json;
const char *text = "{\"foo\": 1} garbage";
if(json_loads(text, 0, &error))
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)
if (!json)
fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
json_decref(json);
}
static void decode_any()
{
static void decode_any() {
json_t *json;
json_error_t error;
@@ -87,8 +105,7 @@ static void decode_any()
json_decref(json);
}
static void decode_int_as_real()
{
static void decode_int_as_real() {
json_t *json;
json_error_t error;
@@ -97,6 +114,8 @@ static void decode_int_as_real()
json_int_t expected;
#endif
char big[311];
json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
if (!json || !json_is_real(json) || json_real_value(json) != 42.0)
fail("json_load decode int as real failed - int");
@@ -107,36 +126,45 @@ static void decode_int_as_real()
imprecise = "9007199254740993";
expected = 9007199254740992ll;
json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY,
&error);
json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
if (!json || !json_is_real(json) || expected != (json_int_t)json_real_value(json))
fail("json_load decode int as real failed - expected imprecision");
json_decref(json);
#endif
/* 1E309 overflows. Here we create 1E309 as a decimal number, i.e.
1000...(309 zeroes)...0. */
big[0] = '1';
memset(big + 1, '0', 309);
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 ||
json_error_code(&error) != json_error_numeric_overflow)
fail("json_load decode int as real failed - expected overflow");
json_decref(json);
}
static void allow_nul()
{
static void allow_nul() {
const char *text = "\"nul byte \\u0000 in string\"";
const char *expected = "nul byte \0 in string";
size_t len = 20;
json_t *json;
json = json_loads(text, JSON_ALLOW_NUL | JSON_DECODE_ANY, NULL);
if(!json || !json_is_string(json))
if (!json || !json_is_string(json))
fail("unable to decode embedded NUL byte");
if(json_string_length(json) != len)
if (json_string_length(json) != len)
fail("decoder returned wrong string length");
if(memcmp(json_string_value(json), expected, len + 1))
if (memcmp(json_string_value(json), expected, len + 1))
fail("decoder returned wrong string content");
json_decref(json);
}
static void load_wrong_args()
{
static void load_wrong_args() {
json_t *json;
json_error_t error;
@@ -152,31 +180,53 @@ static void load_wrong_args()
if (json)
fail("json_loadf should return NULL if the first argument is NULL");
json = json_loadfd(-1, 0, &error);
if (json)
fail("json_loadfd should return NULL if the first argument is < 0");
json = json_load_file(NULL, 0, &error);
if (json)
fail("json_loadf should return NULL if the first argument is NULL");
fail("json_load_file should return NULL if the first argument is NULL");
}
static void position()
{
static void position() {
json_t *json;
size_t flags = JSON_DISABLE_EOF_CHECK;
json_error_t error;
json = json_loads("{\"foo\": \"bar\"}", 0, &error);
if(error.position != 14)
if (error.position != 14)
fail("json_loads returned a wrong position");
json_decref(json);
json = json_loads("{\"foo\": \"bar\"} baz quux", flags, &error);
if(error.position != 14)
if (error.position != 14)
fail("json_loads returned a wrong position");
json_decref(json);
}
static void run_tests()
{
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();
very_long_file_name();
reject_duplicates();
disable_eof_check();
decode_any();
@@ -184,4 +234,5 @@ static void run_tests()
allow_nul();
load_wrong_args();
position();
error_code();
}

View File

@@ -5,10 +5,10 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"
#include <jansson.h>
#include <stdlib.h>
#include <string.h>
struct my_source {
const char *buf;
@@ -18,8 +18,7 @@ struct my_source {
static const char my_str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
static size_t greedy_reader(void *buf, size_t buflen, void *arg)
{
static size_t greedy_reader(void *buf, size_t buflen, void *arg) {
struct my_source *s = arg;
if (buflen > s->cap - s->off)
buflen = s->cap - s->off;
@@ -32,8 +31,7 @@ static size_t greedy_reader(void *buf, size_t buflen, void *arg)
}
}
static void run_tests()
{
static void run_tests() {
struct my_source s;
json_t *json;
json_error_t error;
@@ -55,21 +53,25 @@ static void run_tests()
json = json_load_callback(greedy_reader, &s, 0, &error);
if (json) {
json_decref(json);
fail("json_load_callback should have failed on an incomplete stream, but it didn't");
fail("json_load_callback should have failed on an incomplete stream, "
"but it didn't");
}
if (strcmp(error.source, "<callback>") != 0) {
fail("json_load_callback returned an invalid error source");
}
if (strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_load_callback returned an invalid error message for an unclosed top-level array");
fail("json_load_callback returned an invalid error message for an "
"unclosed top-level array");
}
json = json_load_callback(NULL, NULL, 0, &error);
if (json) {
json_decref(json);
fail("json_load_callback should have failed on NULL load callback, but it didn't");
fail("json_load_callback should have failed on NULL load callback, but "
"it didn't");
}
if (strcmp(error.text, "wrong arguments") != 0) {
fail("json_load_callback returned an invalid error message for a NULL load callback");
fail("json_load_callback returned an invalid error message for a NULL "
"load callback");
}
}

View File

@@ -1,23 +1,22 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "util.h"
#include <jansson.h>
#include <string.h>
#include "util.h"
static void run_tests()
{
static void run_tests() {
json_t *json;
json_error_t error;
const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage";
size_t len = strlen(str) - strlen("garbage");
json = json_loadb(str, len, 0, &error);
if(!json) {
if (!json) {
fail("json_loadb failed on a valid JSON buffer");
}
json_decref(json);
@@ -25,12 +24,14 @@ static void run_tests()
json = json_loadb(str, len - 1, 0, &error);
if (json) {
json_decref(json);
fail("json_loadb should have failed on an incomplete buffer, but it didn't");
fail("json_loadb should have failed on an incomplete buffer, but it "
"didn't");
}
if(error.line != 1) {
if (error.line != 1) {
fail("json_loadb returned an invalid line number on fail");
}
if(strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_loadb returned an invalid error message for an unclosed top-level array");
if (strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_loadb returned an invalid error message for an unclosed "
"top-level array");
}
}

View File

@@ -1,48 +1,78 @@
#include <string.h>
#include <jansson.h>
#include <string.h>
#include "util.h"
static int malloc_called = 0;
static int free_called = 0;
static size_t malloc_used = 0;
/* helper */
static void create_and_free_complex_object()
{
/* helpers */
static void create_and_free_complex_object() {
json_t *obj;
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]",
"foo", 42,
"bar",
"baz", 1,
"qux", 0,
"alice", "bar", "baz",
"bob", 9, 8, 7);
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]}", "foo", 42, "bar", "baz", 1,
"qux", 0, "alice", "bar", "baz", "bob", 9, 8, 7);
json_decref(obj);
}
static void *my_malloc(size_t size)
{
static void create_and_free_object_with_oom() {
int i;
char key[4];
json_t *obj = json_object();
for (i = 0; i < 10; i++) {
snprintf(key, sizeof key, "%d", i);
json_object_set_new(obj, key, json_integer(i));
}
json_decref(obj);
}
static void *my_malloc(size_t size) {
malloc_called = 1;
return malloc(size);
}
static void my_free(void *ptr)
{
static void my_free(void *ptr) {
free_called = 1;
free(ptr);
}
static void test_simple()
{
static void test_simple() {
json_malloc_t mfunc = NULL;
json_free_t ffunc = NULL;
json_set_alloc_funcs(my_malloc, my_free);
json_get_alloc_funcs(&mfunc, &ffunc);
create_and_free_complex_object();
if(malloc_called != 1 || free_called != 1)
if (malloc_called != 1 || free_called != 1 || mfunc != my_malloc || ffunc != my_free)
fail("Custom allocation failed");
}
static void *oom_malloc(size_t size) {
if (malloc_used + size > 800)
return NULL;
malloc_used += size;
return malloc(size);
}
static void oom_free(void *ptr) {
free_called++;
free(ptr);
}
static void test_oom() {
free_called = 0;
json_set_alloc_funcs(oom_malloc, oom_free);
create_and_free_object_with_oom();
if (free_called == 0)
fail("Allocation with OOM failed");
}
/*
Test the secure memory functions code given in the API reference
@@ -50,33 +80,36 @@ static void test_simple()
guaranteed_memset().
*/
static void *secure_malloc(size_t size)
{
static void *secure_malloc(size_t size) {
/* Store the memory area size in the beginning of the block */
void *ptr = malloc(size + 8);
*((size_t *)ptr) = size;
return (char *)ptr + 8;
}
static void secure_free(void *ptr)
{
static void secure_free(void *ptr) {
size_t size;
ptr = (char *)ptr - 8;
size = *((size_t *)ptr);
/*guaranteed_*/memset(ptr, 0, size + 8);
/*guaranteed_*/ memset(ptr, 0, size + 8);
free(ptr);
}
static void test_secure_funcs(void)
{
static void test_secure_funcs(void) {
json_set_alloc_funcs(secure_malloc, secure_free);
create_and_free_complex_object();
}
static void run_tests()
{
static void test_bad_args(void) {
/* The result of this test is not crashing. */
json_get_alloc_funcs(NULL, NULL);
}
static void run_tests() {
test_simple();
test_secure_funcs();
test_oom();
test_bad_args();
}

View File

@@ -1,16 +1,76 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <math.h>
#include <jansson.h>
#include "util.h"
#include <jansson.h>
#include <math.h>
static void run_tests()
{
#ifdef INFINITY
// This test triggers "warning C4756: overflow in constant arithmetic"
// in Visual Studio. This warning is triggered here by design, so disable it.
// (This can only be done on function level so we keep these tests separate)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4756)
#endif
static void test_inifity() {
json_t *real = json_real(INFINITY);
if (real != NULL)
fail("could construct a real from Inf");
real = json_real(1.0);
if (json_real_set(real, INFINITY) != -1)
fail("could set a real to Inf");
if (json_real_value(real) != 1.0)
fail("real value changed unexpectedly");
json_decref(real);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
}
#endif // INFINITY
static void test_bad_args(void) {
json_t *txt = json_string("test");
if (json_integer_value(NULL) != 0)
fail("json_integer_value did not return 0 for non-integer");
if (json_integer_value(txt) != 0)
fail("json_integer_value did not return 0 for non-integer");
if (!json_integer_set(NULL, 0))
fail("json_integer_set did not return error for non-integer");
if (!json_integer_set(txt, 0))
fail("json_integer_set did not return error for non-integer");
if (json_real_value(NULL) != 0.0)
fail("json_real_value did not return 0.0 for non-real");
if (json_real_value(txt) != 0.0)
fail("json_real_value did not return 0.0 for non-real");
if (!json_real_set(NULL, 0.0))
fail("json_real_set did not return error for non-real");
if (!json_real_set(txt, 0.0))
fail("json_real_set did not return error for non-real");
if (json_number_value(NULL) != 0.0)
fail("json_number_value did not return 0.0 for non-numeric");
if (json_number_value(txt) != 0.0)
fail("json_number_value did not return 0.0 for non-numeric");
if (txt->refcount != 1)
fail("unexpected reference count for txt");
json_decref(txt);
}
static void run_tests() {
json_t *integer, *real;
json_int_t i;
double d;
@@ -18,24 +78,24 @@ static void run_tests()
integer = json_integer(5);
real = json_real(100.1);
if(!integer)
if (!integer)
fail("unable to create integer");
if(!real)
if (!real)
fail("unable to create real");
i = json_integer_value(integer);
if(i != 5)
if (i != 5)
fail("wrong integer value");
d = json_real_value(real);
if(d != 100.1)
if (d != 100.1)
fail("wrong real value");
d = json_number_value(integer);
if(d != 5.0)
if (d != 5.0)
fail("wrong number value");
d = json_number_value(real);
if(d != 100.1)
if (d != 100.1)
fail("wrong number value");
json_decref(integer);
@@ -43,31 +103,21 @@ static void run_tests()
#ifdef NAN
real = json_real(NAN);
if(real != NULL)
if (real != NULL)
fail("could construct a real from NaN");
real = json_real(1.0);
if(json_real_set(real, NAN) != -1)
if (json_real_set(real, NAN) != -1)
fail("could set a real to NaN");
if(json_real_value(real) != 1.0)
if (json_real_value(real) != 1.0)
fail("real value changed unexpectedly");
json_decref(real);
#endif
#ifdef INFINITY
real = json_real(INFINITY);
if(real != NULL)
fail("could construct a real from Inf");
real = json_real(1.0);
if(json_real_set(real, INFINITY) != -1)
fail("could set a real to Inf");
if(json_real_value(real) != 1.0)
fail("real value changed unexpectedly");
json_decref(real);
test_inifity();
#endif
test_bad_args();
}

View File

@@ -1,47 +1,43 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "util.h"
#include <jansson.h>
#include <string.h>
#include "util.h"
static void test_clear()
{
static void test_clear() {
json_t *object, *ten;
object = json_object();
ten = json_integer(10);
if(!object)
if (!object)
fail("unable to create object");
if(!ten)
if (!ten)
fail("unable to create integer");
if(json_object_set(object, "a", ten) ||
json_object_set(object, "b", ten) ||
json_object_set(object, "c", ten) ||
json_object_set(object, "d", ten) ||
json_object_set(object, "e", ten))
if (json_object_set(object, "a", ten) || json_object_set(object, "b", ten) ||
json_object_set(object, "c", ten) || json_object_set(object, "d", ten) ||
json_object_set(object, "e", ten))
fail("unable to set value");
if(json_object_size(object) != 5)
if (json_object_size(object) != 5)
fail("invalid size");
json_object_clear(object);
if(json_object_size(object) != 0)
if (json_object_size(object) != 0)
fail("invalid size after clear");
json_decref(ten);
json_decref(object);
}
static void test_update()
{
static void test_update() {
json_t *object, *other, *nine, *ten;
object = json_object();
@@ -50,157 +46,303 @@ static void test_update()
nine = json_integer(9);
ten = json_integer(10);
if(!object || !other)
if (!object || !other)
fail("unable to create object");
if(!nine || !ten)
if (!nine || !ten)
fail("unable to create integer");
/* update an empty object with an empty object */
if(json_object_update(object, other))
fail("unable to update an emtpy object with an empty object");
if (json_object_update(object, other))
fail("unable to update an empty object with an empty object");
if(json_object_size(object) != 0)
if (json_object_size(object) != 0)
fail("invalid size after update");
if(json_object_size(other) != 0)
if (json_object_size(other) != 0)
fail("invalid size for updater after update");
/* update an empty object with a nonempty object */
if(json_object_set(other, "a", ten) ||
json_object_set(other, "b", ten) ||
json_object_set(other, "c", ten) ||
json_object_set(other, "d", ten) ||
json_object_set(other, "e", ten))
if (json_object_set(other, "a", ten) || json_object_set(other, "b", ten) ||
json_object_set(other, "c", ten) || json_object_set(other, "d", ten) ||
json_object_set(other, "e", ten))
fail("unable to set value");
if(json_object_update(object, other))
if (json_object_update(object, other))
fail("unable to update an empty object");
if(json_object_size(object) != 5)
if (json_object_size(object) != 5)
fail("invalid size after update");
if(json_object_get(object, "a") != ten ||
json_object_get(object, "b") != ten ||
json_object_get(object, "c") != ten ||
json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten)
if (json_object_get(object, "a") != ten || json_object_get(object, "b") != ten ||
json_object_get(object, "c") != ten || json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten)
fail("update works incorrectly");
/* perform the same update again */
if(json_object_update(object, other))
if (json_object_update(object, other))
fail("unable to update a non-empty object");
if(json_object_size(object) != 5)
if (json_object_size(object) != 5)
fail("invalid size after update");
if(json_object_get(object, "a") != ten ||
json_object_get(object, "b") != ten ||
json_object_get(object, "c") != ten ||
json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten)
if (json_object_get(object, "a") != ten || json_object_get(object, "b") != ten ||
json_object_get(object, "c") != ten || json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten)
fail("update works incorrectly");
/* update a nonempty object with a nonempty object with both old
and new keys */
if(json_object_clear(other))
if (json_object_clear(other))
fail("clear failed");
if(json_object_set(other, "a", nine) ||
json_object_set(other, "b", nine) ||
json_object_set(other, "f", nine) ||
json_object_set(other, "g", nine) ||
json_object_set(other, "h", nine))
if (json_object_set(other, "a", nine) || json_object_set(other, "b", nine) ||
json_object_set(other, "f", nine) || json_object_set(other, "g", nine) ||
json_object_set(other, "h", nine))
fail("unable to set value");
if(json_object_update(object, other))
if (json_object_update(object, other))
fail("unable to update a nonempty object");
if(json_object_size(object) != 8)
if (json_object_size(object) != 8)
fail("invalid size after update");
if(json_object_get(object, "a") != nine ||
json_object_get(object, "b") != nine ||
json_object_get(object, "f") != nine ||
json_object_get(object, "g") != nine ||
json_object_get(object, "h") != nine)
if (json_object_get(object, "a") != nine || json_object_get(object, "b") != nine ||
json_object_get(object, "f") != nine || json_object_get(object, "g") != nine ||
json_object_get(object, "h") != nine)
fail("update works incorrectly");
/* update_new check */
if (json_object_clear(object))
fail("clear failed");
if (json_object_set(object, "a", ten) || json_object_set(object, "b", ten) ||
json_object_set(object, "c", ten) || json_object_set(object, "d", ten) ||
json_object_set(object, "e", ten))
fail("unable to set value");
if (json_object_update_new(
object, json_pack("{s:O, s:O, s:O}", "b", nine, "f", nine, "g", nine)))
fail("unable to update_new a nonempty object");
if (json_object_size(object) != 7)
fail("invalid size after update_new");
if (json_object_get(object, "a") != ten || json_object_get(object, "b") != nine ||
json_object_get(object, "c") != ten || json_object_get(object, "d") != ten ||
json_object_get(object, "e") != ten || json_object_get(object, "f") != nine ||
json_object_get(object, "g") != nine)
fail("update_new works incorrectly");
json_decref(nine);
json_decref(ten);
json_decref(other);
json_decref(object);
}
static void test_conditional_updates()
{
static void test_set_many_keys() {
json_t *object, *value;
const char *keys = "abcdefghijklmnopqrstuvwxyz";
char buf[2];
size_t i;
object = json_object();
if (!object)
fail("unable to create object");
value = json_string("a");
if (!value)
fail("unable to create string");
buf[1] = '\0';
for (i = 0; i < strlen(keys); i++) {
buf[0] = keys[i];
if (json_object_set(object, buf, value))
fail("unable to set object key");
}
json_decref(object);
json_decref(value);
}
static void test_conditional_updates() {
json_t *object, *other;
object = json_pack("{sisi}", "foo", 1, "bar", 2);
other = json_pack("{sisi}", "foo", 3, "baz", 4);
if(json_object_update_existing(object, other))
if (json_object_update_existing(object, other))
fail("json_object_update_existing failed");
if(json_object_size(object) != 2)
if (json_object_size(object) != 2)
fail("json_object_update_existing added new items");
if(json_integer_value(json_object_get(object, "foo")) != 3)
if (json_integer_value(json_object_get(object, "foo")) != 3)
fail("json_object_update_existing failed to update existing key");
if(json_integer_value(json_object_get(object, "bar")) != 2)
if (json_integer_value(json_object_get(object, "bar")) != 2)
fail("json_object_update_existing updated wrong key");
json_decref(object);
/* json_object_update_existing_new check */
object = json_pack("{sisi}", "foo", 1, "bar", 2);
if (json_object_update_existing_new(object, json_pack("{sisi}", "foo", 3, "baz", 4)))
fail("json_object_update_existing_new failed");
if (json_object_size(object) != 2)
fail("json_object_update_existing_new added new items");
if (json_integer_value(json_object_get(object, "foo")) != 3)
fail("json_object_update_existing_new failed to update existing key");
if (json_integer_value(json_object_get(object, "bar")) != 2)
fail("json_object_update_existing_new updated wrong key");
json_decref(object);
object = json_pack("{sisi}", "foo", 1, "bar", 2);
if(json_object_update_missing(object, other))
if (json_object_update_missing(object, other))
fail("json_object_update_missing failed");
if(json_object_size(object) != 3)
if (json_object_size(object) != 3)
fail("json_object_update_missing didn't add new items");
if(json_integer_value(json_object_get(object, "foo")) != 1)
if (json_integer_value(json_object_get(object, "foo")) != 1)
fail("json_object_update_missing updated existing key");
if(json_integer_value(json_object_get(object, "bar")) != 2)
if (json_integer_value(json_object_get(object, "bar")) != 2)
fail("json_object_update_missing updated wrong key");
if(json_integer_value(json_object_get(object, "baz")) != 4)
if (json_integer_value(json_object_get(object, "baz")) != 4)
fail("json_object_update_missing didn't add new items");
json_decref(object);
/* json_object_update_missing_new check */
object = json_pack("{sisi}", "foo", 1, "bar", 2);
if (json_object_update_missing_new(object, json_pack("{sisi}", "foo", 3, "baz", 4)))
fail("json_object_update_missing_new failed");
if (json_object_size(object) != 3)
fail("json_object_update_missing_new didn't add new items");
if (json_integer_value(json_object_get(object, "foo")) != 1)
fail("json_object_update_missing_new updated existing key");
if (json_integer_value(json_object_get(object, "bar")) != 2)
fail("json_object_update_missing_new updated wrong key");
if (json_integer_value(json_object_get(object, "baz")) != 4)
fail("json_object_update_missing_new didn't add new items");
json_decref(object);
json_decref(other);
}
static void test_circular()
{
static void test_recursive_updates() {
json_t *invalid, *object, *other, *barBefore, *barAfter;
invalid = json_integer(42);
object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2);
other = json_pack("{sisisi}", "foo", 3, "bar", 4, "baz", 5);
if (!json_object_update_recursive(invalid, other))
fail("json_object_update_recursive accepted non-object argument");
json_decref(invalid);
if (json_object_update_recursive(object, other))
fail("json_object_update_recursive failed");
if (json_object_size(object) != 3)
fail("invalid size after update");
if (json_integer_value(json_object_get(object, "foo")) != 3)
fail("json_object_update_recursive failed to update existing key");
if (json_integer_value(json_object_get(object, "bar")) != 4)
fail("json_object_update_recursive failed to overwrite object");
if (json_integer_value(json_object_get(object, "baz")) != 5)
fail("json_object_update_recursive didn't add new item");
json_decref(object);
json_decref(other);
object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2);
other = json_pack("{s{si}}", "bar", "baz", 3);
barBefore = json_object_get(object, "bar");
if (!barBefore)
fail("can't get bar object before json_object_update_recursive");
if (json_object_update_recursive(object, other))
fail("json_object_update_recursive failed");
if (json_object_size(object) != 2)
fail("invalid size after update");
if (!json_object_get(object, "foo"))
fail("json_object_update_recursive removed existing key");
if (json_integer_value(json_object_get(json_object_get(object, "bar"), "baz")) != 3)
fail("json_object_update_recursive failed to update nested value");
barAfter = json_object_get(object, "bar");
if (!barAfter)
fail("can't get bar object after json_object_update_recursive");
if (barBefore != barAfter)
fail("bar object reference changed after json_object_update_recursive");
json_decref(object);
json_decref(other);
/* check circular reference */
object = json_pack("{s{s{s{si}}}}", "foo", "bar", "baz", "xxx", 2);
other = json_pack("{s{s{si}}}", "foo", "bar", "baz", 2);
json_object_set(json_object_get(json_object_get(other, "foo"), "bar"), "baz",
json_object_get(other, "foo"));
if (!json_object_update_recursive(object, other))
fail("json_object_update_recursive update a circular reference!");
json_object_set_new(json_object_get(json_object_get(other, "foo"), "bar"), "baz",
json_integer(1));
if (json_object_update_recursive(object, other))
fail("json_object_update_recursive failed!");
json_decref(object);
json_decref(other);
}
static void test_circular() {
json_t *object1, *object2;
object1 = json_object();
object2 = json_object();
if(!object1 || !object2)
if (!object1 || !object2)
fail("unable to create object");
/* the simple case is checked */
if(json_object_set(object1, "a", object1) == 0)
if (json_object_set(object1, "a", object1) == 0)
fail("able to set self");
/* create circular references */
if(json_object_set(object1, "a", object2) ||
json_object_set(object2, "a", object1))
if (json_object_set(object1, "a", object2) || json_object_set(object2, "a", object1))
fail("unable to set value");
/* circularity is detected when dumping */
if(json_dumps(object1, 0) != NULL)
if (json_dumps(object1, 0) != NULL)
fail("able to dump circulars");
/* decref twice to deal with the circular references */
@@ -209,141 +351,114 @@ static void test_circular()
json_decref(object1);
}
static void test_set_nocheck()
{
static void test_set_nocheck() {
json_t *object, *string;
object = json_object();
string = json_string("bar");
if(!object)
if (!object)
fail("unable to create object");
if(!string)
if (!string)
fail("unable to create string");
if(json_object_set_nocheck(object, "foo", string))
if (json_object_set_nocheck(object, "foo", string))
fail("json_object_set_nocheck failed");
if(json_object_get(object, "foo") != string)
if (json_object_get(object, "foo") != string)
fail("json_object_get after json_object_set_nocheck failed");
/* invalid UTF-8 in key */
if(json_object_set_nocheck(object, "a\xefz", string))
if (json_object_set_nocheck(object, "a\xefz", string))
fail("json_object_set_nocheck failed for invalid UTF-8");
if(json_object_get(object, "a\xefz") != string)
if (json_object_get(object, "a\xefz") != string)
fail("json_object_get after json_object_set_nocheck failed");
if(json_object_set_new_nocheck(object, "bax", json_integer(123)))
if (json_object_set_new_nocheck(object, "bax", json_integer(123)))
fail("json_object_set_new_nocheck failed");
if(json_integer_value(json_object_get(object, "bax")) != 123)
if (json_integer_value(json_object_get(object, "bax")) != 123)
fail("json_object_get after json_object_set_new_nocheck failed");
/* invalid UTF-8 in key */
if(json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321)))
if (json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321)))
fail("json_object_set_new_nocheck failed for invalid UTF-8");
if(json_integer_value(json_object_get(object, "asdf\xfe")) != 321)
if (json_integer_value(json_object_get(object, "asdf\xfe")) != 321)
fail("json_object_get after json_object_set_new_nocheck failed");
json_decref(string);
json_decref(object);
}
static void test_iterators()
{
int i;
static void test_iterators() {
json_t *object, *foo, *bar, *baz;
const char *iter_keys[3];
int have_key[3] = { 0, 0, 0 };
json_t *iter_values[3];
void *iter;
if(json_object_iter(NULL))
if (json_object_iter(NULL))
fail("able to iterate over NULL");
if(json_object_iter_next(NULL, NULL))
if (json_object_iter_next(NULL, NULL))
fail("able to increment an iterator on a NULL object");
object = json_object();
foo = json_string("foo");
bar = json_string("bar");
baz = json_string("baz");
if(!object || !foo || !bar || !bar)
if (!object || !foo || !bar || !baz)
fail("unable to create values");
if(json_object_iter_next(object, NULL))
if (json_object_iter_next(object, NULL))
fail("able to increment a NULL iterator");
if(json_object_set(object, "a", foo) ||
json_object_set(object, "b", bar) ||
json_object_set(object, "c", baz))
if (json_object_set(object, "a", foo) || json_object_set(object, "b", bar) ||
json_object_set(object, "c", baz))
fail("unable to populate object");
iter = json_object_iter(object);
if(!iter)
if (!iter)
fail("unable to get iterator");
iter_keys[0] = json_object_iter_key(iter);
iter_values[0] = json_object_iter_value(iter);
if (strcmp(json_object_iter_key(iter), "a") != 0)
fail("iterating doesn't yield keys in order");
if (json_object_iter_value(iter) != foo)
fail("iterating doesn't yield values in order");
iter = json_object_iter_next(object, iter);
if(!iter)
if (!iter)
fail("unable to increment iterator");
iter_keys[1] = json_object_iter_key(iter);
iter_values[1] = json_object_iter_value(iter);
if (strcmp(json_object_iter_key(iter), "b") != 0)
fail("iterating doesn't yield keys in order");
if (json_object_iter_value(iter) != bar)
fail("iterating doesn't yield values in order");
iter = json_object_iter_next(object, iter);
if(!iter)
if (!iter)
fail("unable to increment iterator");
iter_keys[2] = json_object_iter_key(iter);
iter_values[2] = json_object_iter_value(iter);
if (strcmp(json_object_iter_key(iter), "c") != 0)
fail("iterating doesn't yield keys in order");
if (json_object_iter_value(iter) != baz)
fail("iterating doesn't yield values in order");
if(json_object_iter_next(object, iter) != NULL)
if (json_object_iter_next(object, iter) != NULL)
fail("able to iterate over the end");
/* Check that keys have correct values */
for (i = 0; i < 3; i++) {
if (strcmp(iter_keys[i], "a") == 0) {
if (iter_values[i] != foo)
fail("wrong value for iter key a");
else
have_key[0] = 1;
} else if (strcmp(iter_keys[i], "b") == 0) {
if (iter_values[i] != bar)
fail("wrong value for iter key b");
else
have_key[1] = 1;
} else if (strcmp(iter_keys[i], "c") == 0) {
if (iter_values[i] != baz)
fail("wrong value for iter key c");
else
have_key[2] = 1;
}
}
/* Check that we got all keys */
for(i = 0; i < 3; i++) {
if(!have_key[i])
fail("a key wasn't iterated over");
}
if(json_object_iter_at(object, "foo"))
if (json_object_iter_at(object, "foo"))
fail("json_object_iter_at() succeeds for non-existent key");
iter = json_object_iter_at(object, "b");
if(!iter)
if (!iter)
fail("json_object_iter_at() fails for an existing key");
if(strcmp(json_object_iter_key(iter), "b"))
if (strcmp(json_object_iter_key(iter), "b"))
fail("iterating failed: wrong key");
if(json_object_iter_value(iter) != bar)
if (json_object_iter_value(iter) != bar)
fail("iterating failed: wrong value");
if(json_object_iter_set(object, iter, baz))
if (json_object_iter_set(object, iter, baz))
fail("unable to set value at iterator");
if(strcmp(json_object_iter_key(iter), "b"))
if (strcmp(json_object_iter_key(iter), "b"))
fail("json_object_iter_key() fails after json_object_iter_set()");
if(json_object_iter_value(iter) != baz)
if (json_object_iter_value(iter) != baz)
fail("json_object_iter_value() fails after json_object_iter_set()");
if(json_object_get(object, "b") != baz)
if (json_object_get(object, "b") != baz)
fail("json_object_get() fails after json_object_iter_set()");
json_decref(object);
@@ -352,107 +467,109 @@ static void test_iterators()
json_decref(baz);
}
static void test_misc()
{
static void test_misc() {
json_t *object, *string, *other_string, *value;
object = json_object();
string = json_string("test");
other_string = json_string("other");
if(!object)
if (!object)
fail("unable to create object");
if(!string || !other_string)
if (!string || !other_string)
fail("unable to create string");
if(json_object_get(object, "a"))
if (json_object_get(object, "a"))
fail("value for nonexisting key");
if(json_object_set(object, "a", string))
if (json_object_set(object, "a", string))
fail("unable to set value");
if(!json_object_set(object, NULL, string))
if (!json_object_set(object, NULL, string))
fail("able to set NULL key");
if(!json_object_set(object, "a", NULL))
if (json_object_del(object, "a"))
fail("unable to del the only key");
if (json_object_set(object, "a", string))
fail("unable to set value");
if (!json_object_set(object, "a", NULL))
fail("able to set NULL value");
/* invalid UTF-8 in key */
if(!json_object_set(object, "a\xefz", string))
if (!json_object_set(object, "a\xefz", string))
fail("able to set invalid unicode key");
value = json_object_get(object, "a");
if(!value)
if (!value)
fail("no value for existing key");
if(value != string)
if (value != string)
fail("got different value than what was added");
/* "a", "lp" and "px" collide in a five-bucket hashtable */
if(json_object_set(object, "b", string) ||
json_object_set(object, "lp", string) ||
json_object_set(object, "px", string))
if (json_object_set(object, "b", string) || json_object_set(object, "lp", string) ||
json_object_set(object, "px", string))
fail("unable to set value");
value = json_object_get(object, "a");
if(!value)
if (!value)
fail("no value for existing key");
if(value != string)
if (value != string)
fail("got different value than what was added");
if(json_object_set(object, "a", other_string))
if (json_object_set(object, "a", other_string))
fail("unable to replace an existing key");
value = json_object_get(object, "a");
if(!value)
if (!value)
fail("no value for existing key");
if(value != other_string)
if (value != other_string)
fail("got different value than what was set");
if(!json_object_del(object, "nonexisting"))
if (!json_object_del(object, "nonexisting"))
fail("able to delete a nonexisting key");
if(json_object_del(object, "px"))
if (json_object_del(object, "px"))
fail("unable to delete an existing key");
if(json_object_del(object, "a"))
if (json_object_del(object, "a"))
fail("unable to delete an existing key");
if(json_object_del(object, "lp"))
if (json_object_del(object, "lp"))
fail("unable to delete an existing key");
/* add many keys to initiate rehashing */
if(json_object_set(object, "a", string))
if (json_object_set(object, "a", string))
fail("unable to set value");
if(json_object_set(object, "lp", string))
if (json_object_set(object, "lp", string))
fail("unable to set value");
if(json_object_set(object, "px", string))
if (json_object_set(object, "px", string))
fail("unable to set value");
if(json_object_set(object, "c", string))
if (json_object_set(object, "c", string))
fail("unable to set value");
if(json_object_set(object, "d", string))
if (json_object_set(object, "d", string))
fail("unable to set value");
if(json_object_set(object, "e", string))
if (json_object_set(object, "e", string))
fail("unable to set value");
if(json_object_set_new(object, "foo", json_integer(123)))
if (json_object_set_new(object, "foo", json_integer(123)))
fail("unable to set new value");
value = json_object_get(object, "foo");
if(!json_is_integer(value) || json_integer_value(value) != 123)
if (!json_is_integer(value) || json_integer_value(value) != 123)
fail("json_object_set_new works incorrectly");
if(!json_object_set_new(object, NULL, json_integer(432)))
if (!json_object_set_new(object, NULL, json_integer(432)))
fail("able to set_new NULL key");
if(!json_object_set_new(object, "foo", NULL))
if (!json_object_set_new(object, "foo", NULL))
fail("able to set_new NULL value");
json_decref(string);
@@ -460,12 +577,12 @@ static void test_misc()
json_decref(object);
}
static void test_preserve_order()
{
static void test_preserve_order() {
json_t *object;
char *result;
const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": 3, \"sit amet\": 5, \"helicopter\": 7}";
const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": "
"3, \"sit amet\": 5, \"helicopter\": 7}";
object = json_object();
@@ -486,7 +603,7 @@ static void test_preserve_order()
result = json_dumps(object, JSON_PRESERVE_ORDER);
if(strcmp(expected, result) != 0) {
if (strcmp(expected, result) != 0) {
fprintf(stderr, "%s != %s", expected, result);
fail("JSON_PRESERVE_ORDER doesn't work");
}
@@ -495,33 +612,186 @@ static void test_preserve_order()
json_decref(object);
}
static void test_object_foreach()
{
static void test_object_foreach() {
const char *key;
json_t *object1, *object2, *value;
object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
object2 = json_object();
json_object_foreach(object1, key, value)
json_object_set(object2, key, value);
json_object_foreach(object1, key, value) json_object_set(object2, key, value);
if(!json_equal(object1, object2))
if (!json_equal(object1, object2))
fail("json_object_foreach failed to iterate all key-value pairs");
json_decref(object1);
json_decref(object2);
}
static void run_tests()
{
static void test_object_foreach_safe() {
const char *key;
void *tmp;
json_t *object, *value;
object = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
json_object_foreach_safe(object, tmp, key, value) { json_object_del(object, key); }
if (json_object_size(object) != 0)
fail("json_object_foreach_safe failed to iterate all key-value pairs");
json_decref(object);
}
static void test_bad_args(void) {
json_t *obj = json_object();
json_t *num = json_integer(1);
void *iter;
if (!obj || !num)
fail("failed to allocate test objects");
if (json_object_set(obj, "testkey", json_null()))
fail("failed to set testkey on object");
iter = json_object_iter(obj);
if (!iter)
fail("failed to retrieve test iterator");
if (json_object_size(NULL) != 0)
fail("json_object_size with non-object argument returned non-zero");
if (json_object_size(num) != 0)
fail("json_object_size with non-object argument returned non-zero");
if (json_object_get(NULL, "test") != NULL)
fail("json_object_get with non-object argument returned non-NULL");
if (json_object_get(num, "test") != NULL)
fail("json_object_get with non-object argument returned non-NULL");
if (json_object_get(obj, NULL) != NULL)
fail("json_object_get with NULL key returned non-NULL");
if (!json_object_set_new_nocheck(NULL, "test", json_null()))
fail("json_object_set_new_nocheck with non-object argument did not "
"return error");
if (!json_object_set_new_nocheck(num, "test", json_null()))
fail("json_object_set_new_nocheck with non-object argument did not "
"return error");
if (!json_object_set_new_nocheck(obj, "test", json_incref(obj)))
fail("json_object_set_new_nocheck with object == value did not return "
"error");
if (!json_object_set_new_nocheck(obj, NULL, json_object()))
fail("json_object_set_new_nocheck with NULL key did not return error");
if (!json_object_del(NULL, "test"))
fail("json_object_del with non-object argument did not return error");
if (!json_object_del(num, "test"))
fail("json_object_del with non-object argument did not return error");
if (!json_object_del(obj, NULL))
fail("json_object_del with NULL key did not return error");
if (!json_object_clear(NULL))
fail("json_object_clear with non-object argument did not return error");
if (!json_object_clear(num))
fail("json_object_clear with non-object argument did not return error");
if (!json_object_update(NULL, obj))
fail("json_object_update with non-object first argument did not return "
"error");
if (!json_object_update(num, obj))
fail("json_object_update with non-object first argument did not return "
"error");
if (!json_object_update(obj, NULL))
fail("json_object_update with non-object second argument did not "
"return error");
if (!json_object_update(obj, num))
fail("json_object_update with non-object second argument did not "
"return error");
if (!json_object_update_existing(NULL, obj))
fail("json_object_update_existing with non-object first argument did "
"not return error");
if (!json_object_update_existing(num, obj))
fail("json_object_update_existing with non-object first argument did "
"not return error");
if (!json_object_update_existing(obj, NULL))
fail("json_object_update_existing with non-object second argument did "
"not return error");
if (!json_object_update_existing(obj, num))
fail("json_object_update_existing with non-object second argument did "
"not return error");
if (!json_object_update_missing(NULL, obj))
fail("json_object_update_missing with non-object first argument did "
"not return error");
if (!json_object_update_missing(num, obj))
fail("json_object_update_missing with non-object first argument did "
"not return error");
if (!json_object_update_missing(obj, NULL))
fail("json_object_update_missing with non-object second argument did "
"not return error");
if (!json_object_update_missing(obj, num))
fail("json_object_update_missing with non-object second argument did "
"not return error");
if (json_object_iter(NULL) != NULL)
fail("json_object_iter with non-object argument returned non-NULL");
if (json_object_iter(num) != NULL)
fail("json_object_iter with non-object argument returned non-NULL");
if (json_object_iter_at(NULL, "test") != NULL)
fail("json_object_iter_at with non-object argument returned non-NULL");
if (json_object_iter_at(num, "test") != NULL)
fail("json_object_iter_at with non-object argument returned non-NULL");
if (json_object_iter_at(obj, NULL) != NULL)
fail("json_object_iter_at with NULL iter returned non-NULL");
if (json_object_iter_next(obj, NULL) != NULL)
fail("json_object_iter_next with NULL iter returned non-NULL");
if (json_object_iter_next(num, iter) != NULL)
fail("json_object_iter_next with non-object argument returned non-NULL");
if (json_object_iter_key(NULL) != NULL)
fail("json_object_iter_key with NULL iter returned non-NULL");
if (json_object_key_to_iter(NULL) != NULL)
fail("json_object_key_to_iter with NULL iter returned non-NULL");
if (json_object_iter_value(NULL) != NULL)
fail("json_object_iter_value with NULL iter returned non-NULL");
if (!json_object_iter_set_new(NULL, iter, json_incref(num)))
fail("json_object_iter_set_new with non-object argument did not return "
"error");
if (!json_object_iter_set_new(num, iter, json_incref(num)))
fail("json_object_iter_set_new with non-object argument did not return "
"error");
if (!json_object_iter_set_new(obj, NULL, json_incref(num)))
fail("json_object_iter_set_new with NULL iter did not return error");
if (!json_object_iter_set_new(obj, iter, NULL))
fail("json_object_iter_set_new with NULL value did not return error");
if (obj->refcount != 1)
fail("unexpected reference count for obj");
if (num->refcount != 1)
fail("unexpected reference count for num");
json_decref(obj);
json_decref(num);
}
static void run_tests() {
test_misc();
test_clear();
test_update();
test_set_many_keys();
test_conditional_updates();
test_recursive_updates();
test_circular();
test_set_nocheck();
test_iterators();
test_preserve_order();
test_object_foreach();
test_object_foreach_safe();
test_bad_args();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
*
* Jansson is free software; you can redistribute it and/or modify
@@ -12,13 +12,45 @@
#include <jansson_config.h>
#include <string.h>
#include <jansson.h>
#include <stdio.h>
#include "util.h"
#include <jansson.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
static void run_tests()
{
#ifdef INFINITY
// This test triggers "warning C4756: overflow in constant arithmetic"
// in Visual Studio. This warning is triggered here by design, so disable it.
// (This can only be done on function level so we keep these tests separate)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4756)
#endif
static void test_inifity() {
json_error_t error;
if (json_pack_ex(&error, 0, "f", INFINITY))
fail("json_pack infinity incorrectly succeeded");
check_error(json_error_numeric_overflow, "Invalid floating point value", "<args>", 1,
1, 1);
if (json_pack_ex(&error, 0, "[f]", INFINITY))
fail("json_pack infinity array element incorrectly succeeded");
check_error(json_error_numeric_overflow, "Invalid floating point value", "<args>", 1,
2, 2);
if (json_pack_ex(&error, 0, "{s:f}", "key", INFINITY))
fail("json_pack infinity object value incorrectly succeeded");
check_error(json_error_numeric_overflow, "Invalid floating point value", "<args>", 1,
4, 4);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
}
#endif // INFINITY
static void run_tests() {
json_t *value;
int i;
char buffer[4] = {'t', 'e', 's', 't'};
@@ -29,197 +61,335 @@ static void run_tests()
*/
/* true */
value = json_pack("b", 1);
if(!json_is_true(value))
if (!json_is_true(value))
fail("json_pack boolean failed");
if(value->refcount != (size_t)-1)
if (value->refcount != (size_t)-1)
fail("json_pack boolean refcount failed");
json_decref(value);
/* false */
value = json_pack("b", 0);
if(!json_is_false(value))
if (!json_is_false(value))
fail("json_pack boolean failed");
if(value->refcount != (size_t)-1)
if (value->refcount != (size_t)-1)
fail("json_pack boolean refcount failed");
json_decref(value);
/* null */
value = json_pack("n");
if(!json_is_null(value))
if (!json_is_null(value))
fail("json_pack null failed");
if(value->refcount != (size_t)-1)
if (value->refcount != (size_t)-1)
fail("json_pack null refcount failed");
json_decref(value);
/* integer */
value = json_pack("i", 1);
if(!json_is_integer(value) || json_integer_value(value) != 1)
if (!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack integer failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* integer from json_int_t */
value = json_pack("I", (json_int_t)555555);
if(!json_is_integer(value) || json_integer_value(value) != 555555)
if (!json_is_integer(value) || json_integer_value(value) != 555555)
fail("json_pack json_int_t failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* real */
value = json_pack("f", 1.0);
if(!json_is_real(value) || json_real_value(value) != 1.0)
if (!json_is_real(value) || json_real_value(value) != 1.0)
fail("json_pack real failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack real refcount failed");
json_decref(value);
/* string */
value = json_pack("s", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string refcount failed");
json_decref(value);
/* nullable string (defined case) */
value = json_pack("s?", "test");
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack nullable string (defined case) failed");
if (value->refcount != (size_t)1)
fail("json_pack nullable string (defined case) refcount failed");
json_decref(value);
/* nullable string (NULL case) */
value = json_pack("s?", NULL);
if (!json_is_null(value))
fail("json_pack nullable string (NULL case) failed");
if (value->refcount != (size_t)-1)
fail("json_pack nullable string (NULL case) refcount failed");
json_decref(value);
/* nullable string concatenation */
if (json_pack_ex(&error, 0, "s?+", "test", "ing"))
fail("json_pack failed to catch invalid format 's?+'");
check_error(json_error_invalid_format, "Cannot use '+' on optional strings",
"<format>", 1, 2, 2);
/* nullable string with integer length */
if (json_pack_ex(&error, 0, "s?#", "test", 4))
fail("json_pack failed to catch invalid format 's?#'");
check_error(json_error_invalid_format, "Cannot use '#' on optional strings",
"<format>", 1, 2, 2);
/* string and length (int) */
value = json_pack("s#", "test asdf", 4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string and length refcount failed");
json_decref(value);
/* string and length (size_t) */
value = json_pack("s%", "test asdf", (size_t)4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string and length refcount failed");
json_decref(value);
/* string and length (int), non-NUL terminated string */
value = json_pack("s#", buffer, 4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length (int) failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string and length (int) refcount failed");
json_decref(value);
/* string and length (size_t), non-NUL terminated string */
value = json_pack("s%", buffer, (size_t)4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length (size_t) failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string and length (size_t) refcount failed");
json_decref(value);
/* string concatenation */
if (json_pack("s+", "test", NULL))
fail("json_pack string concatenation succeeded with NULL string");
value = json_pack("s++", "te", "st", "ing");
if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
if (!json_is_string(value) || strcmp("testing", json_string_value(value)))
fail("json_pack string concatenation failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string concatenation refcount failed");
json_decref(value);
/* string concatenation and length (int) */
value = json_pack("s#+#+", "test", 1, "test", 2, "test");
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
if (!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
fail("json_pack string concatenation and length (int) failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack string concatenation and length (int) refcount failed");
json_decref(value);
/* string concatenation and length (size_t) */
value = json_pack("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
if (!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
fail("json_pack string concatenation and length (size_t) failed");
if(value->refcount != (size_t)1)
fail("json_pack string concatenation and length (size_t) refcount failed");
if (value->refcount != (size_t)1)
fail("json_pack string concatenation and length (size_t) refcount "
"failed");
json_decref(value);
/* empty object */
value = json_pack("{}", 1.0);
if(!json_is_object(value) || json_object_size(value) != 0)
if (!json_is_object(value) || json_object_size(value) != 0)
fail("json_pack empty object failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack empty object refcount failed");
json_decref(value);
/* empty list */
value = json_pack("[]", 1.0);
if(!json_is_array(value) || json_array_size(value) != 0)
if (!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty list failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack empty list failed");
json_decref(value);
/* non-incref'd object */
value = json_pack("o", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
if (!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed");
if(value->refcount != (size_t)1)
if (value->refcount != (size_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* non-incref'd nullable object (defined case) */
value = json_pack("o?", json_integer(1));
if (!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack nullable object (defined case) failed");
if (value->refcount != (size_t)1)
fail("json_pack nullable object (defined case) refcount failed");
json_decref(value);
/* non-incref'd nullable object (NULL case) */
value = json_pack("o?", NULL);
if (!json_is_null(value))
fail("json_pack nullable object (NULL case) failed");
if (value->refcount != (size_t)-1)
fail("json_pack nullable object (NULL case) refcount failed");
json_decref(value);
/* incref'd object */
value = json_pack("O", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
if (!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed");
if(value->refcount != (size_t)2)
if (value->refcount != (size_t)2)
fail("json_pack integer refcount failed");
json_decref(value);
json_decref(value);
/* incref'd nullable object (defined case) */
value = json_pack("O?", json_integer(1));
if (!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack incref'd nullable object (defined case) failed");
if (value->refcount != (size_t)2)
fail("json_pack incref'd nullable object (defined case) refcount "
"failed");
json_decref(value);
json_decref(value);
/* incref'd nullable object (NULL case) */
value = json_pack("O?", NULL);
if (!json_is_null(value))
fail("json_pack incref'd nullable object (NULL case) failed");
if (value->refcount != (size_t)-1)
fail("json_pack incref'd nullable object (NULL case) refcount failed");
/* simple object */
value = json_pack("{s:[]}", "foo");
if(!json_is_object(value) || json_object_size(value) != 1)
if (!json_is_object(value) || json_object_size(value) != 1)
fail("json_pack array failed");
if(!json_is_array(json_object_get(value, "foo")))
if (!json_is_array(json_object_get(value, "foo")))
fail("json_pack array failed");
if(json_object_get(value, "foo")->refcount != (size_t)1)
if (json_object_get(value, "foo")->refcount != (size_t)1)
fail("json_pack object refcount failed");
json_decref(value);
/* object with complex key */
value = json_pack("{s+#+: []}", "foo", "barbar", 3, "baz");
if(!json_is_object(value) || json_object_size(value) != 1)
if (!json_is_object(value) || json_object_size(value) != 1)
fail("json_pack array failed");
if(!json_is_array(json_object_get(value, "foobarbaz")))
if (!json_is_array(json_object_get(value, "foobarbaz")))
fail("json_pack array failed");
if(json_object_get(value, "foobarbaz")->refcount != (size_t)1)
if (json_object_get(value, "foobarbaz")->refcount != (size_t)1)
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");
if (json_pack_ex(&error, 0, "{s:i*}", "a", 1))
fail("json_pack object optional invalid incorrectly succeeded");
check_error(json_error_invalid_format, "Expected format 's', got '*'", "<format>", 1,
5, 5);
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);
value = json_pack("{s:s*}", "key", "\xff\xff");
if (value)
fail("json_pack object optional with invalid UTF-8 incorrectly "
"succeeded");
if (json_pack_ex(&error, 0, "{s: s*#}", "key", "test", 1))
fail("json_pack failed to catch invalid format 's*#'");
check_error(json_error_invalid_format, "Cannot use '#' on optional strings",
"<format>", 1, 6, 6);
if (json_pack_ex(&error, 0, "{s: s*+}", "key", "test", "ing"))
fail("json_pack failed to catch invalid format 's*+'");
check_error(json_error_invalid_format, "Cannot use '+' on optional strings",
"<format>", 1, 6, 6);
/* simple array */
value = json_pack("[i,i,i]", 0, 1, 2);
if(!json_is_array(value) || json_array_size(value) != 3)
if (!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack object failed");
for(i=0; i<3; i++)
{
if(!json_is_integer(json_array_get(value, i)) ||
json_integer_value(json_array_get(value, i)) != i)
for (i = 0; i < 3; i++) {
if (!json_is_integer(json_array_get(value, i)) ||
json_integer_value(json_array_get(value, i)) != i)
fail("json_pack integer array failed");
}
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");
if (json_pack_ex(&error, 0, "[i*]", 1))
fail("json_pack array optional invalid incorrectly succeeded");
check_error(json_error_invalid_format, "Unexpected format character '*'", "<format>",
1, 3, 3);
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);
#ifdef NAN
/* Invalid float values */
if (json_pack_ex(&error, 0, "f", NAN))
fail("json_pack NAN incorrectly succeeded");
check_error(json_error_numeric_overflow, "Invalid floating point value", "<args>", 1,
1, 1);
if (json_pack_ex(&error, 0, "[f]", NAN))
fail("json_pack NAN array element incorrectly succeeded");
check_error(json_error_numeric_overflow, "Invalid floating point value", "<args>", 1,
2, 2);
if (json_pack_ex(&error, 0, "{s:f}", "key", NAN))
fail("json_pack NAN object value incorrectly succeeded");
check_error(json_error_numeric_overflow, "Invalid floating point value", "<args>", 1,
4, 4);
#endif
#ifdef INFINITY
test_inifity();
#endif
/* Whitespace; regular string */
value = json_pack(" s ", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
value = json_pack(" s\t ", "test");
if (!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string (with whitespace) failed");
json_decref(value);
/* Whitespace; empty array */
value = json_pack("[ ]");
if(!json_is_array(value) || json_array_size(value) != 0)
if (!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty array (with whitespace) failed");
json_decref(value);
/* Whitespace; array */
value = json_pack("[ i , i, i ] ", 1, 2, 3);
if(!json_is_array(value) || json_array_size(value) != 3)
if (!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack array (with whitespace) failed");
json_decref(value);
@@ -228,80 +398,150 @@ static void run_tests()
*/
/* newline in format string */
if(json_pack_ex(&error, 0, "{\n\n1"))
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, "[}"))
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, "{]"))
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, "["))
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, "{"))
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))
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))
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))
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", "<args>", 1, 1, 1);
/* + on its own */
if(json_pack_ex(&error, 0, "+", NULL))
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);
/* Empty format */
if (json_pack_ex(&error, 0, ""))
fail("json_pack failed to catch empty format string");
check_error(json_error_invalid_argument, "NULL or empty format string", "<format>",
-1, -1, 0);
/* NULL format */
if(json_pack_ex(&error, 0, NULL))
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))
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 object key", "<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", "<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"))
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))
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, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"))
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))
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"))
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);
/* Invalid UTF-8 in an optional '?' string */
if (json_pack_ex(&error, 0, "{s:s?}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in an optional '?' "
"string");
check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "<args>", 1, 5, 5);
/* Invalid UTF-8 in an optional '*' string */
if (json_pack_ex(&error, 0, "{s:s*}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in an optional '*' "
"string");
check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "<args>", 1, 5, 5);
/* Invalid UTF-8 in a concatenated key */
if (json_pack_ex(&error, 0, "{s+:i}", "\xff\xff", "concat", 42))
fail("json_pack failed to catch invalid UTF-8 in an object key");
check_error(json_error_invalid_utf8, "Invalid UTF-8 object key", "<args>", 1, 3, 3);
if (json_pack_ex(&error, 0, "{s:o}", "foo", NULL))
fail("json_pack failed to catch nullable object");
check_error(json_error_null_value, "NULL object", "<args>", 1, 4, 4);
if (json_pack_ex(&error, 0, "{s:O}", "foo", NULL))
fail("json_pack failed to catch nullable incref object");
check_error(json_error_null_value, "NULL object", "<args>", 1, 4, 4);
if (json_pack_ex(&error, 0, "{s+:o}", "foo", "bar", NULL))
fail("json_pack failed to catch non-nullable object value");
check_error(json_error_null_value, "NULL object", "<args>", 1, 5, 5);
if (json_pack_ex(&error, 0, "[1s", "Hi"))
fail("json_pack failed to catch invalid format");
check_error(json_error_invalid_format, "Unexpected format character '1'", "<format>",
1, 2, 2);
if (json_pack_ex(&error, 0, "[1s+", "Hi", "ya"))
fail("json_pack failed to catch invalid format");
check_error(json_error_invalid_format, "Unexpected format character '1'", "<format>",
1, 2, 2);
if (json_pack_ex(&error, 0, "[so]", NULL, json_object()))
fail("json_pack failed to catch NULL value");
check_error(json_error_null_value, "NULL string", "<args>", 1, 2, 2);
}

View File

@@ -1,92 +1,138 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include <jansson.h>
#include "util.h"
#include <jansson.h>
#include <string.h>
static void test_bad_args(void) {
json_t *num = json_integer(1);
json_t *txt = json_string("test");
if (!num || !txt)
fail("failed to allocate test objects");
if (json_string_nocheck(NULL) != NULL)
fail("json_string_nocheck with NULL argument did not return NULL");
if (json_stringn_nocheck(NULL, 0) != NULL)
fail("json_stringn_nocheck with NULL argument did not return NULL");
if (json_string(NULL) != NULL)
fail("json_string with NULL argument did not return NULL");
if (json_stringn(NULL, 0) != NULL)
fail("json_stringn with NULL argument did not return NULL");
if (json_string_length(NULL) != 0)
fail("json_string_length with non-string argument did not return 0");
if (json_string_length(num) != 0)
fail("json_string_length with non-string argument did not return 0");
if (json_string_value(NULL) != NULL)
fail("json_string_value with non-string argument did not return NULL");
if (json_string_value(num) != NULL)
fail("json_string_value with non-string argument did not return NULL");
if (!json_string_setn_nocheck(NULL, "", 0))
fail("json_string_setn with non-string argument did not return error");
if (!json_string_setn_nocheck(num, "", 0))
fail("json_string_setn with non-string argument did not return error");
if (!json_string_setn_nocheck(txt, NULL, 0))
fail("json_string_setn_nocheck with NULL value did not return error");
if (!json_string_set_nocheck(txt, NULL))
fail("json_string_set_nocheck with NULL value did not return error");
if (!json_string_set(txt, NULL))
fail("json_string_set with NULL value did not return error");
if (!json_string_setn(txt, NULL, 0))
fail("json_string_setn with NULL value did not return error");
if (num->refcount != 1)
fail("unexpected reference count for num");
if (txt->refcount != 1)
fail("unexpected reference count for txt");
json_decref(num);
json_decref(txt);
}
/* Call the simple functions not covered by other tests of the public API */
static void run_tests()
{
static void run_tests() {
json_t *value;
value = json_boolean(1);
if(!json_is_true(value))
if (!json_is_true(value))
fail("json_boolean(1) failed");
json_decref(value);
value = json_boolean(-123);
if(!json_is_true(value))
if (!json_is_true(value))
fail("json_boolean(-123) failed");
json_decref(value);
value = json_boolean(0);
if(!json_is_false(value))
if (!json_is_false(value))
fail("json_boolean(0) failed");
if(json_boolean_value(value) != 0)
if (json_boolean_value(value) != 0)
fail("json_boolean_value failed");
json_decref(value);
value = json_integer(1);
if(json_typeof(value) != JSON_INTEGER)
if (json_typeof(value) != JSON_INTEGER)
fail("json_typeof failed");
if(json_is_object(value))
if (json_is_object(value))
fail("json_is_object failed");
if(json_is_array(value))
if (json_is_array(value))
fail("json_is_array failed");
if(json_is_string(value))
if (json_is_string(value))
fail("json_is_string failed");
if(!json_is_integer(value))
if (!json_is_integer(value))
fail("json_is_integer failed");
if(json_is_real(value))
if (json_is_real(value))
fail("json_is_real failed");
if(!json_is_number(value))
if (!json_is_number(value))
fail("json_is_number failed");
if(json_is_true(value))
if (json_is_true(value))
fail("json_is_true failed");
if(json_is_false(value))
if (json_is_false(value))
fail("json_is_false failed");
if(json_is_boolean(value))
if (json_is_boolean(value))
fail("json_is_boolean failed");
if(json_is_null(value))
if (json_is_null(value))
fail("json_is_null failed");
json_decref(value);
value = json_string("foo");
if(!value)
if (!value)
fail("json_string failed");
if(strcmp(json_string_value(value), "foo"))
if (strcmp(json_string_value(value), "foo"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
if(json_string_set(value, "barr"))
if (json_string_set(value, "barr"))
fail("json_string_set failed");
if(strcmp(json_string_value(value), "barr"))
if (strcmp(json_string_value(value), "barr"))
fail("invalid string value");
if (json_string_length(value) != 4)
fail("invalid string length");
if(json_string_setn(value, "hi\0ho", 5))
if (json_string_setn(value, "hi\0ho", 5))
fail("json_string_set failed");
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
if (memcmp(json_string_value(value), "hi\0ho\0", 6))
fail("invalid string value");
if (json_string_length(value) != 5)
fail("invalid string length");
@@ -94,32 +140,32 @@ static void run_tests()
json_decref(value);
value = json_string(NULL);
if(value)
if (value)
fail("json_string(NULL) failed");
/* invalid UTF-8 */
value = json_string("a\xefz");
if(value)
if (value)
fail("json_string(<invalid utf-8>) failed");
value = json_string_nocheck("foo");
if(!value)
if (!value)
fail("json_string_nocheck failed");
if(strcmp(json_string_value(value), "foo"))
if (strcmp(json_string_value(value), "foo"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
if(json_string_set_nocheck(value, "barr"))
if (json_string_set_nocheck(value, "barr"))
fail("json_string_set_nocheck failed");
if(strcmp(json_string_value(value), "barr"))
if (strcmp(json_string_value(value), "barr"))
fail("invalid string value");
if (json_string_length(value) != 4)
fail("invalid string length");
if(json_string_setn_nocheck(value, "hi\0ho", 5))
if (json_string_setn_nocheck(value, "hi\0ho", 5))
fail("json_string_set failed");
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
if (memcmp(json_string_value(value), "hi\0ho\0", 6))
fail("invalid string value");
if (json_string_length(value) != 5)
fail("invalid string length");
@@ -128,100 +174,114 @@ static void run_tests()
/* invalid UTF-8 */
value = json_string_nocheck("qu\xff");
if(!value)
if (!value)
fail("json_string_nocheck failed");
if(strcmp(json_string_value(value), "qu\xff"))
if (strcmp(json_string_value(value), "qu\xff"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
if(json_string_set_nocheck(value, "\xfd\xfe\xff"))
if (json_string_set_nocheck(value, "\xfd\xfe\xff"))
fail("json_string_set_nocheck failed");
if(strcmp(json_string_value(value), "\xfd\xfe\xff"))
if (strcmp(json_string_value(value), "\xfd\xfe\xff"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
json_decref(value);
value = json_integer(123);
if(!value)
if (!value)
fail("json_integer failed");
if(json_integer_value(value) != 123)
if (json_integer_value(value) != 123)
fail("invalid integer value");
if(json_number_value(value) != 123.0)
if (json_number_value(value) != 123.0)
fail("invalid number value");
if(json_integer_set(value, 321))
if (json_integer_set(value, 321))
fail("json_integer_set failed");
if(json_integer_value(value) != 321)
if (json_integer_value(value) != 321)
fail("invalid integer value");
if(json_number_value(value) != 321.0)
if (json_number_value(value) != 321.0)
fail("invalid number value");
json_decref(value);
value = json_real(123.123);
if(!value)
if (!value)
fail("json_real failed");
if(json_real_value(value) != 123.123)
if (json_real_value(value) != 123.123)
fail("invalid integer value");
if(json_number_value(value) != 123.123)
if (json_number_value(value) != 123.123)
fail("invalid number value");
if(json_real_set(value, 321.321))
if (json_real_set(value, 321.321))
fail("json_real_set failed");
if(json_real_value(value) != 321.321)
if (json_real_value(value) != 321.321)
fail("invalid real value");
if(json_number_value(value) != 321.321)
if (json_number_value(value) != 321.321)
fail("invalid number value");
json_decref(value);
value = json_true();
if(!value)
if (!value)
fail("json_true failed");
json_decref(value);
value = json_false();
if(!value)
if (!value)
fail("json_false failed");
json_decref(value);
value = json_null();
if(!value)
if (!value)
fail("json_null failed");
json_decref(value);
/* Test reference counting on singletons (true, false, null) */
value = json_true();
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
json_decref(value);
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
value = json_false();
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
json_decref(value);
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
value = json_null();
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
json_decref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
if (value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
#ifdef json_auto_t
value = json_string("foo");
{
json_auto_t *test = json_incref(value);
/* Use test so GCC doesn't complain it is unused. */
if (!json_is_string(test))
fail("value type check failed");
}
if (value->refcount != 1)
fail("automatic decrement failed");
json_decref(value);
#endif
test_bad_args();
}

View File

@@ -0,0 +1,29 @@
#include "util.h"
#include <jansson.h>
#include <string.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);
s = json_sprintf("%s", "");
if (!s)
fail("json_sprintf returned NULL");
if (!json_is_string(s))
fail("json_sprintf didn't return a JSON string");
if (json_string_length(s) != 0)
fail("string is not empty");
json_decref(s);
if (json_sprintf("%s", "\xff\xff"))
fail("json_sprintf unexpected success with invalid UTF");
}
static void run_tests() { test_sprintf(); }

View File

@@ -1,18 +1,17 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include "util.h"
#include <jansson.h>
#include <stdio.h>
#include "util.h"
#include <string.h>
static void run_tests()
{
static void run_tests() {
json_t *j, *j2;
int i1, i2, i3;
json_int_t I1;
@@ -29,89 +28,89 @@ static void run_tests()
/* true */
rv = json_unpack(json_true(), "b", &i1);
if(rv || !i1)
if (rv || !i1)
fail("json_unpack boolean failed");
/* false */
rv = json_unpack(json_false(), "b", &i1);
if(rv || i1)
if (rv || i1)
fail("json_unpack boolean failed");
/* null */
if(json_unpack(json_null(), "n"))
if (json_unpack(json_null(), "n"))
fail("json_unpack null failed");
/* integer */
j = json_integer(42);
rv = json_unpack(j, "i", &i1);
if(rv || i1 != 42)
if (rv || i1 != 42)
fail("json_unpack integer failed");
json_decref(j);
/* json_int_t */
j = json_integer(5555555);
rv = json_unpack(j, "I", &I1);
if(rv || I1 != 5555555)
if (rv || I1 != 5555555)
fail("json_unpack json_int_t failed");
json_decref(j);
/* real */
j = json_real(1.7);
rv = json_unpack(j, "f", &f);
if(rv || f != 1.7)
if (rv || f != 1.7)
fail("json_unpack real failed");
json_decref(j);
/* number */
j = json_integer(12345);
rv = json_unpack(j, "F", &f);
if(rv || f != 12345.0)
if (rv || f != 12345.0)
fail("json_unpack (real or) integer failed");
json_decref(j);
j = json_real(1.7);
rv = json_unpack(j, "F", &f);
if(rv || f != 1.7)
if (rv || f != 1.7)
fail("json_unpack real (or integer) failed");
json_decref(j);
/* string */
j = json_string("foo");
rv = json_unpack(j, "s", &s);
if(rv || strcmp(s, "foo"))
if (rv || strcmp(s, "foo"))
fail("json_unpack string failed");
json_decref(j);
/* string with length (size_t) */
j = json_string("foo");
rv = json_unpack(j, "s%", &s, &z);
if(rv || strcmp(s, "foo") || z != 3)
if (rv || strcmp(s, "foo") || z != 3)
fail("json_unpack string with length (size_t) failed");
json_decref(j);
/* empty object */
j = json_object();
if(json_unpack(j, "{}"))
if (json_unpack(j, "{}"))
fail("json_unpack empty object failed");
json_decref(j);
/* empty list */
j = json_array();
if(json_unpack(j, "[]"))
if (json_unpack(j, "[]"))
fail("json_unpack empty list failed");
json_decref(j);
/* non-incref'd object */
j = json_object();
rv = json_unpack(j, "o", &j2);
if(rv || j2 != j || j->refcount != 1)
if (rv || j2 != j || j->refcount != 1)
fail("json_unpack object failed");
json_decref(j);
/* incref'd object */
j = json_object();
rv = json_unpack(j, "O", &j2);
if(rv || j2 != j || j->refcount != 2)
if (rv || j2 != j || j->refcount != 2)
fail("json_unpack object failed");
json_decref(j);
json_decref(j);
@@ -119,21 +118,21 @@ static void run_tests()
/* simple object */
j = json_pack("{s:i}", "foo", 42);
rv = json_unpack(j, "{s:i}", "foo", &i1);
if(rv || i1 != 42)
if (rv || i1 != 42)
fail("json_unpack simple object failed");
json_decref(j);
/* simple array */
j = json_pack("[iii]", 1, 2, 3);
rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
if (rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack simple array failed");
json_decref(j);
/* object with many items & strict checking */
j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3);
rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
if (rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack object with many items failed");
json_decref(j);
@@ -142,130 +141,149 @@ static void run_tests()
*/
j = json_integer(42);
if(!json_unpack_ex(j, &error, 0, "z"))
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]"))
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, "[}"))
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, "{]"))
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, "["))
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, "{"))
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))
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))
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))
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))
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 */
j = json_integer(42);
j2 = json_string("foo");
if(!json_unpack_ex(j, &error, 0, "s"))
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"))
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"))
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"))
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"))
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"))
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"))
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]"))
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"))
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);
/* Array index out of range */
j = json_pack("[i]", 1);
if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
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))
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))
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);
/*
@@ -274,127 +292,140 @@ static void run_tests()
j = json_pack("[iii]", 1, 2, 3);
rv = json_unpack(j, "[iii!]", &i1, &i2, &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
if (rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack array with strict validation failed");
json_decref(j);
j = json_pack("[iii]", 1, 2, 3);
if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2))
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))
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);
rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1);
if(rv || strcmp(s, "bar") != 0 || i1 != 42)
if (rv || strcmp(s, "bar") != 0 || i1 != 42)
fail("json_unpack object with strict validation failed");
json_decref(j);
/* Unpack the same item twice */
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1);
if (!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
fail("json_unpack object with strict validation failed");
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
{
const char *possible_errors[] = {"2 object item(s) left unpacked: baz, quux",
"2 object item(s) left unpacked: quux, baz"};
check_errors(json_error_end_of_input_expected, possible_errors, 2, "<validation>",
1, 10, 10);
}
json_decref(j);
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
if(json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY,
"[i{sisn}[ii]]", "foo", "bar"))
if (json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY, "[i{sisn}[ii]]", "foo",
"bar"))
fail("json_unpack complex value with strict validation failed");
json_decref(j);
/* ! and * must be last */
j = json_pack("[ii]", 1, 2);
if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2))
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))
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))
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))
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"))
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", "<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))
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 */
j = json_object();
i1 = 0;
if(json_unpack(j, "{s?i}", "foo", &i1))
if (json_unpack(j, "{s?i}", "foo", &i1))
fail("json_unpack failed for optional key");
if(i1 != 0)
if (i1 != 0)
fail("json_unpack unpacked an optional key");
json_decref(j);
i1 = 0;
j = json_pack("{si}", "foo", 42);
if(json_unpack(j, "{s?i}", "foo", &i1))
if (json_unpack(j, "{s?i}", "foo", &i1))
fail("json_unpack failed for an optional value");
if(i1 != 42)
if (i1 != 42)
fail("json_unpack failed to unpack an optional value");
json_decref(j);
j = json_object();
i1 = i2 = i3 = 0;
if(json_unpack(j, "{s?[ii]s?{s{si}}}",
"foo", &i1, &i2,
"bar", "baz", "quux", &i3))
if (json_unpack(j, "{s?[ii]s?{s{si}}}", "foo", &i1, &i2, "bar", "baz", "quux", &i3))
fail("json_unpack failed for complex optional values");
if(i1 != 0 || i2 != 0 || i3 != 0)
if (i1 != 0 || i2 != 0 || i3 != 0)
fail("json_unpack unexpectedly unpacked something");
json_decref(j);
j = json_pack("{s{si}}", "foo", "bar", 42);
if(json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1))
if (json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1))
fail("json_unpack failed for complex optional values");
if(i1 != 42)
if (i1 != 42)
fail("json_unpack failed to unpack");
json_decref(j);
/* Combine ? and ! */
j = json_pack("{si}", "foo", 42);
i1 = i2 = 0;
if(json_unpack(j, "{sis?i!}", "foo", &i1, "bar", &i2))
if (json_unpack(j, "{sis?i!}", "foo", &i1, "bar", &i2))
fail("json_unpack failed for optional values with strict mode");
if(i1 != 42)
if (i1 != 42)
fail("json_unpack failed to unpack");
if(i2 != 0)
if (i2 != 0)
fail("json_unpack failed to unpack");
json_decref(j);
/* But don't compensate a missing key with an optional one. */
j = json_pack("{sisi}", "foo", 42, "baz", 43);
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", "<validation>", 1, 8, 8);
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(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz",
"<validation>", 1, 8, 8);
json_decref(j);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2019 Sean Bright <sean.bright@gmail.com>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include "util.h"
#include <jansson.h>
#include <string.h>
static void test_version_str(void) {
if (strcmp(jansson_version_str(), JANSSON_VERSION)) {
fail("jansson_version_str returned invalid version string");
}
}
static void test_version_cmp() {
if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION,
JANSSON_MICRO_VERSION)) {
fail("jansson_version_cmp equality check failed");
}
if (jansson_version_cmp(JANSSON_MAJOR_VERSION - 1, 0, 0) <= 0) {
fail("jansson_version_cmp less than check failed");
}
if (JANSSON_MINOR_VERSION) {
if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION - 1,
JANSSON_MICRO_VERSION) <= 0) {
fail("jansson_version_cmp less than check failed");
}
}
if (JANSSON_MICRO_VERSION) {
if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION,
JANSSON_MICRO_VERSION - 1) <= 0) {
fail("jansson_version_cmp less than check failed");
}
}
if (jansson_version_cmp(JANSSON_MAJOR_VERSION + 1, JANSSON_MINOR_VERSION,
JANSSON_MICRO_VERSION) >= 0) {
fail("jansson_version_cmp greater than check failed");
}
if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION + 1,
JANSSON_MICRO_VERSION) >= 0) {
fail("jansson_version_cmp greater than check failed");
}
if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION,
JANSSON_MICRO_VERSION + 1) >= 0) {
fail("jansson_version_cmp greater than check failed");
}
}
static void run_tests() {
test_version_str();
test_version_cmp();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -20,46 +20,65 @@
#include <jansson.h>
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
#define failhdr fprintf(stderr, "%s:%d: ", __FILE__, __LINE__)
#define fail(msg) \
do { \
failhdr; \
fprintf(stderr, "%s\n", msg); \
exit(1); \
} while(0)
#define fail(msg) \
do { \
failhdr; \
fprintf(stderr, "%s\n", msg); \
exit(1); \
} while (0)
/* Assumes json_error_t error */
#define check_error(text_, source_, line_, column_, position_) \
do { \
if(strcmp(error.text, text_) != 0) { \
failhdr; \
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
exit(1); \
} \
if(strcmp(error.source, source_) != 0) { \
failhdr; \
\
fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \
exit(1); \
} \
if(error.line != line_) { \
failhdr; \
fprintf(stderr, "line: %d != %d\n", error.line, line_); \
exit(1); \
} \
if(error.column != column_) { \
failhdr; \
fprintf(stderr, "column: %d != %d\n", error.column, column_); \
exit(1); \
} \
if(error.position != position_) { \
failhdr; \
fprintf(stderr, "position: %d != %d\n", error.position, position_); \
exit(1); \
} \
} while(0)
#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; \
break; \
} \
} \
if (!found_) { \
failhdr; \
if (num_ == 1) { \
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, texts_[0]); \
} else { \
fprintf(stderr, "text: \"%s\" does not match\n", error.text); \
} \
exit(1); \
} \
if (strcmp(error.source, source_) != 0) { \
failhdr; \
\
fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \
exit(1); \
} \
if (error.line != line_) { \
failhdr; \
fprintf(stderr, "line: %d != %d\n", error.line, line_); \
exit(1); \
} \
if (error.column != column_) { \
failhdr; \
fprintf(stderr, "column: %d != %d\n", error.column, column_); \
exit(1); \
} \
if (error.position != position_) { \
failhdr; \
fprintf(stderr, "position: %d != %d\n", error.position, position_); \
exit(1); \
} \
} while (0)
/* Assumes json_error_t error */
#define check_error(code_, text_, source_, line_, column_, position_) \
check_errors(code_, &text_, 1, source_, line_, column_, position_)
static void run_tests();

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.

View File

@@ -0,0 +1,2 @@
1 5 5
invalid escape near '"\uq'

View File

@@ -0,0 +1 @@
["\uqqqq <-- invalid unicode escape"]

View File

@@ -0,0 +1,2 @@
1 33 33
\u0000 is not allowed without JSON_ALLOW_NUL

View File

@@ -0,0 +1 @@
["null escape \u0000 not allowed"]

View File

@@ -0,0 +1,2 @@
1 2049 2049
maximum parsing depth reached near '['

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More