Compare commits

...

263 commits

Author SHA1 Message Date
Petri Lehtinen
96d160df90
Merge pull request #692 from Andrew-Au/cmake_update/revised/merge
Some checks failed
tests / autotools (clang, no, macos-latest) (push) Has been cancelled
tests / autotools (clang, yes, macos-latest) (push) Has been cancelled
tests / autotools (gcc, no, macos-latest) (push) Has been cancelled
tests / autotools (gcc, yes, macos-latest) (push) Has been cancelled
tests / cmake (clang, macos-latest) (push) Has been cancelled
tests / cmake (gcc, macos-latest) (push) Has been cancelled
tests / cmake (msvc, windows-latest) (push) Has been cancelled
tests / lint (push) Failing after 4s
tests / autotools (clang, no, ubuntu-latest) (push) Failing after 3s
tests / autotools (clang, yes, ubuntu-latest) (push) Failing after 3s
tests / autotools (gcc, no, ubuntu-latest) (push) Failing after 3s
tests / autotools (gcc, yes, ubuntu-latest) (push) Failing after 3s
tests / cmake (clang, ubuntu-latest) (push) Successful in 23s
tests / cmake (gcc, ubuntu-latest) (push) Successful in 20s
tests / valgrind (push) Failing after 17m10s
Use target-based cmake settings
2025-04-04 07:18:04 +03:00
Petri Lehtinen
aef13f87f1 Set minimum cmake version to 3.10 2025-04-04 07:15:20 +03:00
Petri Lehtinen
c16ac732e4
Merge pull request #712 from akheron/fix-lint
Some checks failed
tests / lint (push) Failing after 31s
tests / autotools (clang, no, ubuntu-latest) (push) Failing after 30s
tests / autotools (clang, yes, ubuntu-latest) (push) Failing after 29s
tests / autotools (gcc, no, ubuntu-latest) (push) Failing after 30s
tests / autotools (gcc, yes, ubuntu-latest) (push) Failing after 28s
tests / cmake (clang, ubuntu-latest) (push) Failing after 39s
tests / cmake (gcc, ubuntu-latest) (push) Failing after 39s
tests / valgrind (push) Failing after 35s
tests / cmake (msvc, windows-latest) (push) Has been cancelled
tests / autotools (gcc, yes, macos-latest) (push) Has been cancelled
tests / cmake (clang, macos-latest) (push) Has been cancelled
tests / cmake (gcc, macos-latest) (push) Has been cancelled
tests / autotools (gcc, no, macos-latest) (push) Has been cancelled
tests / autotools (clang, no, macos-latest) (push) Has been cancelled
tests / autotools (clang, yes, macos-latest) (push) Has been cancelled
Fix code formatting
2025-03-23 22:39:12 +02:00
Petri Lehtinen
05a10aa8af Fix code formatting 2025-03-23 22:31:56 +02:00
Petri Lehtinen
4d7ac97b89
Merge pull request #710 from akheron/fix-readthedocs
Add readthedocs config
2025-03-23 21:59:36 +02:00
Petri Lehtinen
23905f372c Add readthedocs config 2025-03-23 21:58:05 +02:00
Petri Lehtinen
ed5cae4ed0 jansson 2.14.1
Some checks failed
tests / lint (push) Failing after 28s
tests / autotools (clang, no, ubuntu-latest) (push) Failing after 32s
tests / autotools (clang, yes, ubuntu-latest) (push) Failing after 30s
tests / autotools (gcc, no, ubuntu-latest) (push) Failing after 39s
tests / autotools (gcc, yes, ubuntu-latest) (push) Failing after 26s
tests / cmake (clang, ubuntu-latest) (push) Failing after 31s
tests / cmake (gcc, ubuntu-latest) (push) Failing after 23s
tests / valgrind (push) Failing after 29s
tests / autotools (gcc, yes, macos-latest) (push) Has been cancelled
tests / cmake (clang, macos-latest) (push) Has been cancelled
tests / cmake (gcc, macos-latest) (push) Has been cancelled
tests / cmake (msvc, windows-latest) (push) Has been cancelled
tests / autotools (clang, no, macos-latest) (push) Has been cancelled
tests / autotools (clang, yes, macos-latest) (push) Has been cancelled
tests / autotools (gcc, no, macos-latest) (push) Has been cancelled
2025-03-23 14:25:44 +02:00
Andrew White
0f9c18dd12 Use target-based cmake settings
- Update minimum required to CMake version 3.5 (versions older than 3.5 are
deprecated as of 3.27)
- update add_definitions to target_compile_definitions
- use target_include_directories for public library includes
- add jansson::jansson alias
2024-07-10 10:31:49 +10:00
Petri Lehtinen
61fc3d0e28
Merge pull request #686 from akheron/readme-badges
Update badges in README
2024-03-31 22:09:15 +03:00
Petri Lehtinen
cce8caba26 Update badges in README 2024-03-31 22:08:10 +03:00
Petri Lehtinen
50953fb1fa
Merge pull request #680 from akheron/dtoa
Use `dtoa()` for optimal encoding of reals
2024-03-31 22:02:09 +03:00
Petri Lehtinen
c780171cf3 Update CHANGES 2024-03-25 20:19:41 +02:00
Petri Lehtinen
2297a2e320 Update LICENSE to add an exception for src/dtoa.c 2024-03-25 20:19:23 +02:00
Petri Lehtinen
f5b3ab323c Add more tests 2024-03-25 20:08:23 +02:00
Petri Lehtinen
9d3abab610 Test with and without dtoa in CI 2024-03-25 20:08:23 +02:00
Petri Lehtinen
9699de8600 Fix tests 2024-03-25 20:08:23 +02:00
Petri Lehtinen
33a6c95d56 Fix compiler warnings 2024-03-25 20:08:23 +02:00
Petri Lehtinen
ed06f65412 Rename a symbol to avoid collision on Windows 2024-03-25 20:08:23 +02:00
Petri Lehtinen
8b975abca1 Use dtoa for double to string conversion 2024-03-25 20:08:23 +02:00
Petri Lehtinen
8660da0f7c
Merge pull request #685 from akheron/simplify-tests
Simplify tests even more
2024-03-25 20:07:53 +02:00
Petri Lehtinen
dcbeb58829 Simplify tests even more
Drop the useless `export` lines from `env` files.
2024-03-25 19:16:34 +02:00
Petri Lehtinen
53383b9e26
Merge pull request #683 from akheron/refactor-tests
Refactor tests
2024-03-21 20:43:31 +02:00
Petri Lehtinen
649c9357c6 Update CHANGES 2024-03-21 20:43:04 +02:00
Petri Lehtinen
73dc6960ad Show ctest output on failure in CI 2024-03-21 20:36:42 +02:00
Petri Lehtinen
88375fb10e Refactor tests to work better with CMake 2024-03-21 20:36:38 +02:00
Petri Lehtinen
0247b5e2e7 Improve clang-format scripts 2024-03-15 21:06:34 +02:00
Petri Lehtinen
842708ac0c
Merge pull request #677 from akheron/ditch-localeconv
Use sprintf() to determine locale's decimal point
2024-03-08 21:53:07 +02:00
Petri Lehtinen
2d1c13224f Use sprintf() to determine locale's decimal point
This should fix thread safety of encoding and decoding, since
localeconv() is not tread safe after all.
2024-03-08 21:36:21 +02:00
Petri Lehtinen
9b9b5e81cf
Merge pull request #679 from akheron/dependabot/github_actions/github-actions-cb1dee63db
Bump the github-actions group with 1 update
2024-03-08 06:42:02 +02:00
dependabot[bot]
0c9c11a89d
Bump the github-actions group with 1 update
Bumps the github-actions group with 1 update: [actions/upload-artifact](https://github.com/actions/upload-artifact).


Updates `actions/upload-artifact` from 3 to 4
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 20:09:47 +00:00
Petri Lehtinen
f52d79a4d3
Merge pull request #672 from pnacht/add-dependabot
Add dependabot, update GitHub Actions
2024-03-07 22:09:25 +02:00
Petri Lehtinen
2f1777ba80
Merge branch 'master' into add-dependabot 2024-03-07 22:08:46 +02:00
Petri Lehtinen
1e57cadbd3
Merge pull request #678 from akheron/ditch-coveralls
Ditch coveralls
2024-03-07 21:54:05 +02:00
Petri Lehtinen
0db4db1048 Ditch coveralls 2024-03-07 21:47:18 +02:00
Pedro Kaj Kjellerup Nacht
73d968feef
Bump GHA versions
Signed-off-by: Pedro Kaj Kjellerup Nacht <pnacht@google.com>
2023-12-06 23:10:14 +00:00
Pedro Kaj Kjellerup Nacht
0154c4af07
Add dependabot to update GHA
Signed-off-by: Pedro Kaj Kjellerup Nacht <pnacht@google.com>
2023-12-06 23:10:03 +00:00
Petri Lehtinen
60097f0096
Create SECURITY.md 2023-06-28 07:42:28 +03:00
Petri Lehtinen
bde28463f8
Merge pull request #646 from Bigpet/patch-1
Remove unused ${SOURCE_DIR}/include include path
2023-03-29 22:07:32 +03:00
Peter Tissen
e7c9ef8e52
Remove unused ${SOURCE_DIR}/include include path
The directory `${CMAKE_CURRENT_SOURCE_DIR}/include` is never created or used. Except in case of an in-source build but then  `${CMAKE_CURRENT_BINARY_DIR}/include` would be the same, so it would only duplicate the correct entry.

Not sure if the intermediary `JANSSON__INCLUDE_DIRS` is still necessary but I thought I'd change as little as possible.

The reason for eliminating this is that when you use CLion and sync into a docker container it will try to copy all include dirs back. Non-existant paths cause warnings every time.
2023-03-29 12:44:28 +02:00
Petri Lehtinen
a22dc95311
Merge pull request #640 from uniontech-lilinjie/master
fix typo
2023-01-11 22:25:45 +02:00
lilinjie
a7d04c8554 fix typo
Signed-off-by: lilinjie <lilinjie@uniontech.com>
2023-01-10 16:35:44 +08:00
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
117 changed files with 11939 additions and 3856 deletions

5
.clang-format Normal file
View file

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

15
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
github-actions:
patterns:
- "*"

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@v4
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts

67
.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,67 @@
name: tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/clang-format-check
autotools:
strategy:
matrix:
os: ["ubuntu-latest", "macos-latest"]
cc: ["gcc", "clang"]
dtoa: ["yes", "no"]
runs-on: ${{ matrix.os }}
steps:
- if: ${{runner.os == 'macOS'}}
run: brew install autoconf automake libtool
- uses: actions/checkout@v4
- run: autoreconf -fi
- env:
CC: ${{ matrix.cc }}
CFLAGS: -Werror
run: ./configure --enable-dtoa=${{ matrix.dtoa }}
- 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@v4
- env:
CC: ${{matrix.cc}}
run: cmake .
- run: cmake --build .
- run: ctest --output-on-failure
valgrind:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: sudo apt update && sudo apt install valgrind
- run: cmake -DJANSSON_TEST_WITH_VALGRIND=ON .
- run: cmake --build .
- run: ctest --output-on-failure

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,22 +0,0 @@
env:
matrix:
- JANSSON_BUILD_METHOD=cmake JANSSON_CMAKE_OPTIONS="-DJANSSON_TEST_WITH_VALGRIND=ON" JANSSON_EXTRA_INSTALL="valgrind"
- JANSSON_BUILD_METHOD=autotools
- JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
language: c
compiler:
- gcc
- clang
matrix:
exclude:
- compiler: clang
env: JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
allow_failures:
- env: JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
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
- if [ "$JANSSON_BUILD_METHOD" = "coverage" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && cmake --build . --target coveralls; fi

189
CHANGES
View file

@ -1,3 +1,190 @@
Version 2.14.1
==============
Released 2025-03-23
* Fixes:
- Fix thread safety of encoding and decoding when `uselocale` or `newlocale`
is used to switch locales inside the threads (#674, #675, #677. Thanks to
Bruno Haible for the report and help with fixing.)
- Use David M. Gay's `dtoa()` algorithm to avoid misprinting issues of real
numbers that are not exactly representable as a `double` (#680).
If this is not desirable, use `./configure --disable-dtoa` or `cmake
-DUSE_DTOA=OFF .`
* Build:
- Make test output nicer in CMake based builds (#683)
- Simplify tests (#685)
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
============
@ -451,7 +638,7 @@ Released 2011-10-06
- Fix identifier decoding under non-UTF-8 locales. (#35)
- `json_load_file()`: Open the input file in binary mode for maximum
compatiblity.
compatibility.
* Documentation:

View file

@ -1,59 +1,11 @@
# 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.10)
project(jansson C)
# Options
option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF)
option(USE_URANDOM "Use /dev/urandom to seed the hash function." ON)
option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." ON)
option(USE_DTOA "Use dtoa for optimal floating-point to string conversions." ON)
if (MSVC)
# This option must match the settings used in your program, in particular if you
@ -65,31 +17,29 @@ 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)
option(JANSSON_COVERALLS "Generate coverage info for Coveralls" OFF)
option(JANSSON_COVERALLS_UPLOAD "Upload coverage info to Coveralls (Only works via Travis)" ON)
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(JANSSON_TEMP_DIR ${PROJECT_BINARY_DIR}/tmp)
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")
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.10")
set(JANSSON_DISPLAY_VERSION "2.14.1")
# This is what is required to match the same numbers as automake's
set(JANSSON_VERSION "4.10.0")
set(JANSSON_VERSION "4.14.0")
set(JANSSON_SOVERSION 4)
# for CheckFunctionKeywords
@ -101,34 +51,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 (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")
endif()
message("C compiler: ${CMAKE_C_COMPILER_ID}")
# Coverage only works with GCC for a debug build.
if (JANSSON_COVERALLS)
set(JANSSON_COVERAGE ON)
endif()
if (JANSSON_COVERAGE)
include(CodeCoverage)
include(Coveralls)
# This adds coverage arguments to gcc/clang.
coveralls_turn_on_coverage()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
endif()
check_include_files (endian.h HAVE_ENDIAN_H)
@ -138,7 +82,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)
@ -150,6 +94,9 @@ check_function_exists (sched_yield HAVE_SCHED_YIELD)
# Check for the int-type includes
check_include_files (stdint.h HAVE_STDINT_H)
include (TestBigEndian)
TEST_BIG_ENDIAN(WORDS_BIGENDIAN)
# Check our 64 bit integer sizes
check_type_size (__int64 __INT64)
check_type_size (int64_t INT64_T)
@ -164,9 +111,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")
@ -182,12 +129,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)
@ -196,12 +143,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)
@ -214,7 +161,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)
@ -250,7 +197,9 @@ 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))
set (JSON_INTEGER_IS_LONG_LONG 1)
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)
@ -272,18 +221,7 @@ if (NOT DEFINED JSON_INT_T)
endif ()
endif ()
# If locale.h and localeconv() are available, define to 1, otherwise to 0.
check_include_files (locale.h HAVE_LOCALE_H)
check_function_exists (localeconv HAVE_LOCALECONV)
if (HAVE_LOCALECONV AND HAVE_LOCALE_H)
set (JSON_HAVE_LOCALECONV 1)
else ()
set (JSON_HAVE_LOCALECONV 0)
endif()
# check if we have setlocale
check_function_exists(setlocale HAVE_SETLOCALE)
# Check what the inline keyword is.
@ -303,8 +241,20 @@ else()
set (JSON_INLINE)
endif()
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS)
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS)
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1); return 0; } " HAVE_SYNC_BUILTINS)
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE); return 0; }" HAVE_ATOMIC_BUILTINS)
if (HAVE_SYNC_BUILTINS)
set(JSON_HAVE_SYNC_BUILTINS 1)
else()
set(JSON_HAVE_SYNC_BUILTINS 0)
endif()
if (HAVE_ATOMIC_BUILTINS)
set(JSON_HAVE_ATOMIC_BUILTINS 1)
else()
set(JSON_HAVE_ATOMIC_BUILTINS 0)
endif()
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.")
@ -316,20 +266,20 @@ configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/)
add_definitions(-DJANSSON_USING_CMAKE)
# configure the private config file
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_private_config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
# and tell the source code to include it
add_definitions(-DHAVE_CONFIG_H)
include_directories (${CMAKE_CURRENT_BINARY_DIR}/include)
include_directories (${CMAKE_CURRENT_BINARY_DIR}/private_include)
# Configuration flags will be set on project later once we have defined the target
# Add the lib sources.
file(GLOB JANSSON_SRC src/*.c)
if (NOT USE_DTOA)
list(FILTER JANSSON_SRC EXCLUDE REGEX ".*dtoa\\.c$")
endif()
set(JANSSON_HDR_PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/hashtable.h
@ -338,7 +288,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)
@ -347,24 +297,78 @@ 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()
# Now target jansson is declared, set per-target values
target_compile_definitions(jansson PUBLIC JANSSON_USING_CMAKE)
target_compile_definitions(jansson PRIVATE HAVE_CONFIG_H)
target_include_directories(jansson
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
INTERFACE $<INSTALL_INTERFACE:include>
)
add_library( jansson::jansson ALIAS jansson )
if (JANSSON_EXAMPLES)
add_executable(simple_parse "${PROJECT_SOURCE_DIR}/examples/simple_parse.c")
add_executable(simple_parse "${CMAKE_CURRENT_SOURCE_DIR}/examples/simple_parse.c")
target_link_libraries(simple_parse jansson)
endif()
@ -374,12 +378,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()
@ -432,7 +436,7 @@ if (JANSSON_BUILD_DOCS)
list(APPEND DOC_TARGETS latex)
endif()
endif()
# The doc target will build all documentation targets.
add_custom_target(doc)
@ -486,16 +490,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
@ -513,7 +521,7 @@ 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}
@ -527,17 +535,22 @@ if (NOT JANSSON_WITHOUT_TESTS)
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)
if ((USE_DTOA AND EXISTS ${TESTDIR}/skip_if_dtoa) OR
(NOT USE_DTOA AND EXISTS ${TESTDIR}/skip_unless_dtoa))
continue()
endif()
if (JANSSON_TEST_WITH_VALGRIND)
add_test(memcheck__${SUITE}__${TNAME}
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR})
@ -560,21 +573,12 @@ if (NOT JANSSON_WITHOUT_TESTS)
endforeach ()
if (JANSSON_COVERAGE)
setup_target_for_coverage(
coverage # Coverage make target "make coverage".
coverage # Name of output directory.
make # Name of test runner executable.
test) # Arguments to the test runner above (make test).
if (JANSSON_COVERALLS)
set(COVERAGE_SRCS ${JANSSON_SRC})
coveralls_setup("${COVERAGE_SRCS}" ${JANSSON_COVERALLS_UPLOAD})
endif ()
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}
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS json_process ${api_tests})
endif ()
@ -599,86 +603,72 @@ set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation
# (We use the same files as ./configure does, so we
# have to defined the same variables used there).
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/${JANSSON_INSTALL_LIB_DIR})
set(exec_prefix "\${prefix}")
set(libdir "\${exec_prefix}/${JANSSON_INSTALL_LIB_DIR}")
set(includedir "\${prefix}/${JANSSON_INSTALL_INCLUDE_DIR}")
set(VERSION ${JANSSON_DISPLAY_VERSION})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
# Make sure the paths are absolute.
# Make sure the paths are relative.
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 "${CMAKE_CURRENT_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)
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.
file(RELATIVE_PATH
REL_INCLUDE_DIR
"${JANSSON_INSTALL_CMAKE_DIR}"
"${JANSSON_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir.
include(CMakePackageConfigHelpers)
# 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)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfigVersion.cmake"
VERSION ${JANSSON_VERSION}
COMPATIBILITY ExactVersion
)
# 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.
configure_package_config_file(
"cmake/janssonConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfig.cmake"
INSTALL_DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}"
)
#
# Install targets.
#
option(JANSSON_INSTALL "Generate installation target" ON)
if (JANSSON_INSTALL)
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)
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.

26
LICENSE
View file

@ -1,4 +1,11 @@
Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
# License
This project is licensed under the MIT license, except where otherwise noted.
The full text of the MIT license is included below.
## MIT License
Copyright (c) 2009-2024 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
@ -17,3 +24,20 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## Exceptions
### `src/dtoa.c`
Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
Permission to use, copy, modify, and distribute this software for any
purpose without fee is hereby granted, provided that this entire notice
is included in all copies of any software which is or includes a copy
or modification of this software and in all copies of the supporting
documentation for such software.
THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.

View file

@ -1,4 +1,4 @@
EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android examples
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,14 +1,10 @@
Jansson README
==============
.. image:: https://travis-ci.org/akheron/jansson.png
:target: https://travis-ci.org/akheron/jansson
.. image:: https://ci.appveyor.com/api/projects/status/lmhkkc4q8cwc65ko
:target: https://ci.appveyor.com/project/akheron/jansson
.. |tests| image:: https://github.com/akheron/jansson/workflows/tests/badge.svg
.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/lmhkkc4q8cwc65ko
.. image:: https://coveralls.io/repos/akheron/jansson/badge.png?branch=master
:target: https://coveralls.io/r/akheron/jansson?branch=master
|tests| |appveyor|
Jansson_ is a C library for encoding, decoding and manipulating JSON
data. Its main features and design principles are:
@ -26,12 +22,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
@ -41,9 +36,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
@ -62,6 +56,14 @@ 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

9
SECURITY.md Normal file
View file

@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
Latest released version.
## Reporting a Vulnerability
Send an email to petri@digip.org.

View file

@ -32,10 +32,6 @@
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1
/* If locale.h and localeconv() are available, define to 1,
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

View file

@ -5,6 +5,10 @@ environment:
- 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

View file

@ -110,10 +110,10 @@ FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner)
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
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."

View file

@ -1,111 +0,0 @@
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
#
#
# Param _COVERAGE_SRCS A list of source files that coverage should be collected for.
# Param _COVERALLS_UPLOAD Upload the result to coveralls?
#
function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD)
# When passing a CMake list to an external process, the list
# will be converted from the format "1;2;3" to "1 2 3".
# This means the script we're calling won't see it as a list
# of sources, but rather just one long path. We remedy this
# by replacing ";" with "*" and then reversing that in the script
# that we're calling.
# http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html
set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS})
set(COVERAGE_SRCS "")
foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP})
set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}")
endforeach()
#message("Coverage sources: ${COVERAGE_SRCS}")
set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json)
add_custom_target(coveralls_generate
# Zero the coverage counters.
COMMAND ${CMAKE_COMMAND}
-P "${PROJECT_SOURCE_DIR}/cmake/CoverallsClear.cmake"
# Run regress tests.
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
# Generate Gcov and translate it into coveralls JSON.
# We do this by executing an external CMake script.
# (We don't want this to run at CMake generation time, but after compilation and everything has run).
COMMAND ${CMAKE_COMMAND}
-DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c"
-DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}"
-DCOV_PATH="${PROJECT_BINARY_DIR}"
-DPROJECT_ROOT="${PROJECT_SOURCE_DIR}"
-P "${PROJECT_SOURCE_DIR}/cmake/CoverallsGenerateGcov.cmake"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Generating coveralls output..."
)
if (_COVERALLS_UPLOAD)
message("COVERALLS UPLOAD: ON")
find_program(CURL_EXECUTABLE curl)
if (NOT CURL_EXECUTABLE)
message(FATAL_ERROR "Coveralls: curl not found! Aborting")
endif()
add_custom_target(coveralls_upload
# Upload the JSON to coveralls.
COMMAND ${CURL_EXECUTABLE}
-S -F json_file=@${COVERALLS_FILE}
https://coveralls.io/api/v1/jobs
DEPENDS coveralls_generate
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Uploading coveralls output...")
add_custom_target(coveralls DEPENDS coveralls_upload)
else()
message("COVERALLS UPLOAD: OFF")
add_custom_target(coveralls DEPENDS coveralls_generate)
endif()
endfunction()
macro(coveralls_turn_on_coverage)
if(NOT (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
AND (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
message(FATAL_ERROR "Coveralls: Compiler ${CMAKE_C_COMPILER_ID} is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake <options> ..")
endif()
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
endmacro()

View file

@ -1,24 +0,0 @@
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
#
file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda)

View file

@ -1,380 +0,0 @@
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
#
# This is intended to be run by a custom target in a CMake project like this.
# 0. Compile program with coverage support.
# 1. Clear coverage data. (Recursively delete *.gcda in build dir)
# 2. Run the unit tests.
# 3. Run this script specifying which source files the coverage should be performed on.
#
# This script will then use gcov to generate .gcov files in the directory specified
# via the COV_PATH var. This should probably be the same as your cmake build dir.
#
# It then parses the .gcov files to convert them into the Coveralls JSON format:
# https://coveralls.io/docs/api
#
# Example for running as standalone CMake script from the command line:
# (Note it is important the -P is at the end...)
# $ cmake -DCOV_PATH=$(pwd)
# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
# -P ../cmake/CoverallsGcovUpload.cmake
#
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
#
# Make sure we have the needed arguments.
#
if (NOT COVERALLS_OUTPUT_FILE)
message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
endif()
if (NOT COV_PATH)
message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
endif()
if (NOT COVERAGE_SRCS)
message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
endif()
if (NOT PROJECT_ROOT)
message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
endif()
# Since it's not possible to pass a CMake list properly in the
# "1;2;3" format to an external process, we have replaced the
# ";" with "*", so reverse that here so we get it back into the
# CMake list format.
string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
find_program(GCOV_EXECUTABLE gcov)
if (NOT GCOV_EXECUTABLE)
message(FATAL_ERROR "gcov not found! Aborting...")
endif()
find_package(Git)
# TODO: Add these git things to the coveralls json.
if (GIT_FOUND)
# Branch.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
macro (git_log_format FORMAT_CHARS VAR_NAME)
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE ${VAR_NAME}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endmacro()
git_log_format(an GIT_AUTHOR_EMAIL)
git_log_format(ae GIT_AUTHOR_EMAIL)
git_log_format(cn GIT_COMMITTER_NAME)
git_log_format(ce GIT_COMMITTER_EMAIL)
git_log_format(B GIT_COMMIT_MESSAGE)
message("Git exe: ${GIT_EXECUTABLE}")
message("Git branch: ${GIT_BRANCH}")
message("Git author: ${GIT_AUTHOR_NAME}")
message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
message("Git commiter name: ${GIT_COMMITTER_NAME}")
message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
message("Git commit message: ${GIT_COMMIT_MESSAGE}")
endif()
############################# Macros #########################################
#
# This macro converts from the full path format gcov outputs:
#
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
#
# to the original source file path the .gcov is for:
#
# /path/to/project/root/subdir/the_file.c
#
macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
# ->
# #path#to#project#root#subdir#the_file.c.gcov
get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
# #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
endmacro()
##############################################################################
# Get the coverage data.
file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
message("GCDA files:")
# Get a list of all the object directories needed by gcov
# (The directories the .gcda files and .o files are found in)
# and run gcov on those.
foreach(GCDA ${GCDA_FILES})
message("Process: ${GCDA}")
message("------------------------------------------------------------------------------")
get_filename_component(GCDA_DIR ${GCDA} PATH)
#
# The -p below refers to "Preserve path components",
# This means that the generated gcov filename of a source file will
# keep the original files entire filepath, but / is replaced with #.
# Example:
#
# /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
# ------------------------------------------------------------------------------
# File '/path/to/project/root/subdir/the_file.c'
# Lines executed:68.34% of 199
# /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
#
# If -p is not specified then the file is named only "the_file.c.gcov"
#
execute_process(
COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
WORKING_DIRECTORY ${COV_PATH}
)
endforeach()
# TODO: Make these be absolute path
file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
# Get only the filenames to use for filtering.
#set(COVERAGE_SRCS_NAMES "")
#foreach (COVSRC ${COVERAGE_SRCS})
# get_filename_component(COVSRC_NAME ${COVSRC} NAME)
# message("${COVSRC} -> ${COVSRC_NAME}")
# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
#endforeach()
#
# Filter out all but the gcov files we want.
#
# We do this by comparing the list of COVERAGE_SRCS filepaths that the
# user wants the coverage data for with the paths of the generated .gcov files,
# so that we only keep the relevant gcov files.
#
# Example:
# COVERAGE_SRCS =
# /path/to/project/root/subdir/the_file.c
#
# ALL_GCOV_FILES =
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
#
# Result should be:
# GCOV_FILES =
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
#
set(GCOV_FILES "")
#message("Look in coverage sources: ${COVERAGE_SRCS}")
message("\nFilter out unwanted GCOV files:")
message("===============================")
set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
foreach (GCOV_FILE ${ALL_GCOV_FILES})
#
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
# ->
# /path/to/project/root/subdir/the_file.c
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
# Is this in the list of source files?
# TODO: We want to match against relative path filenames from the source file root...
list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
if (NOT WAS_FOUND EQUAL -1)
message("YES: ${GCOV_FILE}")
list(APPEND GCOV_FILES ${GCOV_FILE})
# We remove it from the list, so we don't bother searching for it again.
# Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
# have coverage data generated from them (no lines are covered).
list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
else()
message("NO: ${GCOV_FILE}")
endif()
endforeach()
# TODO: Enable setting these
set(JSON_SERVICE_NAME "travis-ci")
set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
set(JSON_TEMPLATE
"{
\"service_name\": \"\@JSON_SERVICE_NAME\@\",
\"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
\"source_files\": \@JSON_GCOV_FILES\@
}"
)
set(SRC_FILE_TEMPLATE
"{
\"name\": \"\@GCOV_SRC_REL_PATH\@\",
\"source\": \"\@GCOV_FILE_SOURCE\@\",
\"coverage\": \@GCOV_FILE_COVERAGE\@
}"
)
message("\nGenerate JSON for files:")
message("=========================")
set(JSON_GCOV_FILES "[")
# Read the GCOV files line by line and get the coverage data.
foreach (GCOV_FILE ${GCOV_FILES})
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
# Loads the gcov file as a list of lines.
file(STRINGS ${GCOV_FILE} GCOV_LINES)
# Instead of trying to parse the source from the
# gcov file, simply read the file contents from the source file.
# (Parsing it from the gcov is hard because C-code uses ; in many places
# which also happens to be the same as the CMake list delimeter).
file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
# According to http://json.org/ these should be escaped as well.
# Don't know how to do that in CMake however...
#string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
#string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
#string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
# We want a json array of coverage data as a single string
# start building them from the contents of the .gcov
set(GCOV_FILE_COVERAGE "[")
foreach (GCOV_LINE ${GCOV_LINES})
# Example of what we're parsing:
# Hitcount |Line | Source
# " 8: 26: if (!allowed || (strlen(allowed) == 0))"
string(REGEX REPLACE
"^([^:]*):([^:]*):(.*)$"
"\\1;\\2;\\3"
RES
"${GCOV_LINE}")
list(LENGTH RES RES_COUNT)
if (RES_COUNT GREATER 2)
list(GET RES 0 HITCOUNT)
list(GET RES 1 LINE)
list(GET RES 2 SOURCE)
string(STRIP ${HITCOUNT} HITCOUNT)
string(STRIP ${LINE} LINE)
# Lines with 0 line numbers are metadata and can be ignored.
if (NOT ${LINE} EQUAL 0)
# Translate the hitcount into valid JSON values.
if (${HITCOUNT} STREQUAL "#####")
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
elseif (${HITCOUNT} STREQUAL "-")
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
else()
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
endif()
# TODO: Look for LCOV_EXCL_LINE in SOURCE to get rid of false positives.
endif()
else()
message(WARNING "Failed to properly parse line --> ${GCOV_LINE}")
endif()
endforeach()
# Advanced way of removing the trailing comma in the JSON array.
# "[1, 2, 3, " -> "[1, 2, 3"
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
# Append the trailing ] to complete the JSON array.
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
# Generate the final JSON for this file.
message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
endforeach()
# Loop through all files we couldn't find any coverage for
# as well, and generate JSON for those as well with 0% coverage.
foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
# Loads the source file as a list of lines.
file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
set(GCOV_FILE_COVERAGE "[")
set(GCOV_FILE_SOURCE "")
foreach (SOURCE ${SRC_LINES})
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
endforeach()
# Remove trailing comma, and complete JSON array with ]
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
# Generate the final JSON for this file.
message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
endforeach()
# Get rid of trailing comma.
string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
# Generate the final complete JSON!
message("Generate final JSON...")
string(CONFIGURE ${JSON_TEMPLATE} JSON)
file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
message("###########################################################################")
message("Generated coveralls JSON containing coverage data:")
message("${COVERALLS_OUTPUT_FILE}")
message("###########################################################################")

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

@ -21,9 +21,10 @@
#define JANSSON_USING_CMAKE
#endif
/* Note: when using cmake, JSON_INTEGER_IS_LONG_LONG is not defined nor used,
* as we will also check for __int64 etc types.
* (the definition was used in the automake system) */
/* If your compiler supports the `long long` type and the strtoll()
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
otherwise to 0. */
#cmakedefine JSON_INTEGER_IS_LONG_LONG 1
/* Bring in the cmake-detected defines */
#cmakedefine HAVE_STDINT_H 1
@ -56,13 +57,16 @@
#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@
/* 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

@ -21,6 +21,8 @@
#cmakedefine HAVE_LOCALE_H 1
#cmakedefine HAVE_SETLOCALE 1
#cmakedefine WORDS_BIGENDIAN 1
#cmakedefine HAVE_INT32_T 1
#ifndef HAVE_INT32_T
# define int32_t @JSON_INT32@
@ -50,4 +52,11 @@
#cmakedefine USE_URANDOM 1
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
#cmakedefine USE_DTOA 1
#if USE_DTOA
# define DTOA_ENABLED 1
#else
# define DTOA_ENABLED 0
#endif
#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@

View file

@ -1,5 +1,5 @@
AC_PREREQ([2.60])
AC_INIT([jansson], [2.10], [petri@digip.org])
AC_INIT([jansson], [2.14.1], [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])
@ -24,6 +25,8 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT8_T
AC_TYPE_LONG_LONG_INT
AC_C_BIGENDIAN
AC_C_INLINE
case $ac_cv_c_inline in
yes) json_inline=inline;;
@ -33,30 +36,38 @@ esac
AC_SUBST([json_inline])
# Checks for library functions.
AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll])
AC_CHECK_FUNCS([close getpid gettimeofday open read setlocale sched_yield strtoll])
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
@ -65,12 +76,6 @@ case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
esac
AC_SUBST([json_have_long_long])
case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
yesyes) json_have_localeconv=1;;
*) json_have_localeconv=0;;
esac
AC_SUBST([json_have_localeconv])
# Features
AC_ARG_ENABLE([urandom],
[AS_HELP_STRING([--disable-urandom],
@ -99,8 +104,74 @@ AC_ARG_ENABLE([initial-hashtable-order],
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([dtoa],
[AS_HELP_STRING([--enable-dtoa], [Use dtoa for optimal floating point to string conversion])],
[case "$enableval" in
yes) dtoa=yes ;;
no) dtoa=no ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-dtoa]) ;;
esac], [dtoa=yes])
if test "$dtoa" = "yes"; then
AC_DEFINE([DTOA_ENABLED], [1],
[Define to 1 to use dtoa to convert floating points to strings])
fi
AM_CONDITIONAL([DTOA_ENABLED], [test "$dtoa" = "yes"])
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])
@ -112,6 +183,7 @@ AC_CONFIG_FILES([
src/jansson_config.h
test/Makefile
test/bin/Makefile
test/ossfuzz/Makefile
test/suites/Makefile
test/suites/api/Makefile
])

9
doc/.readthedocs.yaml Normal file
View file

@ -0,0 +1,9 @@
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.12"
sphinx:
configuration: doc/conf.py

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

View file

@ -58,6 +58,30 @@ the library:
/* Code specific to version 1.3 and above */
#endif
Additionally, there are functions to determine the version of Jansson at
runtime:
.. function:: const char *jansson_version_str()
Return the version of the Jansson library, in the same format as
the ``JANSSON_VERSION`` preprocessor constant.
.. versionadded:: 2.13
.. function:: int jansson_version_cmp(int major, int minor, int micro)
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*.
.. versionadded:: 2.13
``JANSSON_THREAD_SAFE_REFCOUNT``
If this value is defined all read-only operations and reference counting in
Jansson are thread safe. This value is not defined for versions older than
``2.11`` or when the compiler does not provide built-in atomic functions.
Value Representation
====================
@ -90,7 +114,7 @@ also cause errors.
Type
----
.. type:: enum json_type
.. c:enum:: json_type
The type of a JSON value. The following members are defined:
@ -121,33 +145,33 @@ Type
.. function:: int json_typeof(const json_t *json)
Return the type of the JSON value (a :type:`json_type` cast to
:type:`int`). *json* MUST NOT be *NULL*. This function is actually
``int``). *json* MUST NOT be *NULL*. This function is actually
implemented as a macro for speed.
.. function:: json_is_object(const json_t *json)
json_is_array(const json_t *json)
json_is_string(const json_t *json)
json_is_integer(const json_t *json)
json_is_real(const json_t *json)
json_is_true(const json_t *json)
json_is_false(const json_t *json)
json_is_null(const json_t *json)
.. function:: int json_is_object(const json_t *json)
int json_is_array(const json_t *json)
int json_is_string(const json_t *json)
int json_is_integer(const json_t *json)
int json_is_real(const json_t *json)
int json_is_true(const json_t *json)
int json_is_false(const json_t *json)
int json_is_null(const json_t *json)
These functions (actually macros) return true (non-zero) for values
of the given type, and false (zero) for values of other types and
for *NULL*.
.. function:: json_is_number(const json_t *json)
.. function:: int json_is_number(const json_t *json)
Returns true for values of types ``JSON_INTEGER`` and
``JSON_REAL``, and false for other types and for *NULL*.
.. function:: json_is_boolean(const json_t *json)
.. function:: int json_is_boolean(const json_t *json)
Returns true for types ``JSON_TRUE`` and ``JSON_FALSE``, and false
for values of other types and for *NULL*.
.. function:: json_boolean_value(const json_t *json)
.. function:: int json_boolean_value(const json_t *json)
Alias of :func:`json_is_true()`, i.e. returns 1 for ``JSON_TRUE``
and 0 otherwise.
@ -332,6 +356,8 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string`, but with explicit length, so *value* may
contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: json_t *json_string_nocheck(const char *value)
.. refcounting:: new
@ -347,6 +373,8 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string_nocheck`, but with explicit length, so
*value* may contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: const char *json_string_value(const json_t *string)
Returns the associated value of *string* as a null terminated UTF-8
@ -361,6 +389,8 @@ length-aware functions if you wish to embed null bytes in strings.
Returns the length of *string* in its UTF-8 presentation, or zero
if *string* is not a JSON string.
.. versionadded:: 2.7
.. function:: int json_string_set(json_t *string, const char *value)
Sets the associated value of *string* to *value*. *value* must be a
@ -372,6 +402,8 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string_set`, but with explicit length, so *value*
may contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: int json_string_set_nocheck(json_t *string, const char *value)
Like :func:`json_string_set`, but doesn't check that *value* is
@ -384,6 +416,18 @@ length-aware functions if you wish to embed null bytes in strings.
Like :func:`json_string_set_nocheck`, but with explicit length,
so *value* may contain null characters or not be null terminated.
.. versionadded:: 2.7
.. function:: json_t *json_sprintf(const char *format, ...)
json_t *json_vsprintf(const char *format, va_list ap)
.. refcounting:: new
Construct a JSON string from a format string and varargs, just like
:func:`printf()`.
.. versionadded:: 2.11
Number
======
@ -550,12 +594,12 @@ A JSON array is an ordered collection of other JSON values.
Appends all elements in *other_array* to the end of *array*.
Returns 0 on success and -1 on error.
.. function:: json_array_foreach(array, index, value)
.. function:: void json_array_foreach(array, index, value)
Iterate over every element of ``array``, running the block
of code that follows each time with the proper values set to
variables ``index`` and ``value``, of types :type:`size_t` and
:type:`json_t *` respectively. Example::
:type:`json_t` pointer respectively. Example::
/* array is a JSON array */
size_t index;
@ -604,6 +648,15 @@ allowed in object keys.
Get a value corresponding to *key* from *object*. Returns *NULL* if
*key* is not found and on error.
.. function:: json_t *json_object_getn(const json_t *object, const char *key, size_t key_len)
.. refcounting:: borrow
Like :func:`json_object_get`, but give the fixed-length *key* with length *key_len*.
See :ref:`fixed_length_keys` for details.
.. versionadded:: 2.14
.. function:: int json_object_set(json_t *object, const char *key, json_t *value)
Set the value of *key* to *value* in *object*. *key* must be a
@ -611,6 +664,13 @@ allowed in object keys.
already is a value for *key*, it is replaced by the new value.
Returns 0 on success and -1 on error.
.. function:: int json_object_setn(json_t *object, const char *key, size_t key_len, json_t *value)
Like :func:`json_object_set`, but give the fixed-length *key* with length *key_len*.
See :ref:`fixed_length_keys` for details.
.. versionadded:: 2.14
.. function:: int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
Like :func:`json_object_set`, but doesn't check that *key* is
@ -618,12 +678,26 @@ allowed in object keys.
really is the case (e.g. you have already checked it by other
means).
.. function:: int json_object_setn_nocheck(json_t *object, const char *key, size_t key_len, json_t *value)
Like :func:`json_object_set_nocheck`, but give the fixed-length *key* with length *key_len*.
See :ref:`fixed_length_keys` for details.
.. versionadded:: 2.14
.. function:: int json_object_set_new(json_t *object, const char *key, json_t *value)
Like :func:`json_object_set()` but steals the reference to
*value*. This is useful when *value* is newly created and not used
after the call.
.. function:: int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value)
Like :func:`json_object_set_new`, but give the fixed-length *key* with length *key_len*.
See :ref:`fixed_length_keys` for details.
.. versionadded:: 2.14
.. function:: int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value)
Like :func:`json_object_set_new`, but doesn't check that *key* is
@ -631,12 +705,26 @@ allowed in object keys.
really is the case (e.g. you have already checked it by other
means).
.. function:: int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len, json_t *value)
Like :func:`json_object_set_new_nocheck`, but give the fixed-length *key* with length *key_len*.
See :ref:`fixed_length_keys` for details.
.. versionadded:: 2.14
.. function:: int json_object_del(json_t *object, const char *key)
Delete *key* from *object* if it exists. Returns 0 on success, or
-1 if *key* was not found. The reference count of the removed value
is decremented.
.. function:: int json_object_deln(json_t *object, const char *key, size_t key_len)
Like :func:`json_object_del`, but give the fixed-length *key* with length *key_len*.
See :ref:`fixed_length_keys` for details.
.. versionadded:: 2.14
.. function:: int json_object_clear(json_t *object)
Remove all elements from *object*. Returns 0 on success and -1 if
@ -664,12 +752,36 @@ allowed in object keys.
.. versionadded:: 2.3
.. function:: json_object_foreach(object, key, value)
.. function:: int json_object_update_new(json_t *object, json_t *other)
Like :func:`json_object_update()`, but steals the reference to
*other*. This is useful when *other* is newly created and not used
after the call.
.. function:: int json_object_update_existing_new(json_t *object, json_t *other)
Like :func:`json_object_update_new()`, but only the values of existing
keys are updated. No new keys are created. Returns 0 on success or
-1 on error.
.. function:: int json_object_update_missing_new(json_t *object, json_t *other)
Like :func:`json_object_update_new()`, but only new keys are created.
The value of any existing key is not changed. Returns 0 on success
or -1 on error.
.. function:: int json_object_update_recursive(json_t *object, json_t *other)
Like :func:`json_object_update()`, but object values in *other* are
recursively merged with the corresponding values in *object* if they are also
objects, instead of overwriting them. Returns 0 on success or -1 on error.
.. function:: void json_object_foreach(object, key, value)
Iterate over every key-value pair of ``object``, running the block
of code that follows each time with the proper values set to
variables ``key`` and ``value``, of types :type:`const char *` and
:type:`json_t *` respectively. Example::
variables ``key`` and ``value``, of types ``const char *`` and
:type:`json_t` pointer respectively. Example::
/* obj is a JSON object */
const char *key;
@ -682,7 +794,7 @@ allowed in object keys.
The items are returned in the order they were inserted to the
object.
**Note:** It's not safe to call ``json_object_del(object, key)``
**Note:** It's not safe to call ``json_object_del(object, key)`` or ``json_object_deln(object, key, key_len)``
during iteration. If you need to, use
:func:`json_object_foreach_safe` instead.
@ -696,14 +808,42 @@ allowed in object keys.
.. versionadded:: 2.3
.. function:: json_object_foreach_safe(object, tmp, key, value)
.. function:: void json_object_foreach_safe(object, tmp, key, value)
Like :func:`json_object_foreach()`, but it's safe to call
``json_object_del(object, key)`` during iteration. You need to pass
an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
``json_object_del(object, key)`` or ``json_object_deln(object, key, key_len)`` during iteration.
You need to pass an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
.. versionadded:: 2.8
.. function:: void json_object_keylen_foreach(object, key, key_len, value)
Like :c:func:`json_object_foreach`, but in *key_len* stored length of the *key*.
Example::
/* obj is a JSON object */
const char *key;
json_t *value;
size_t len;
json_object_keylen_foreach(obj, key, len, value) {
printf("got key %s with length %zu\n", key, len);
}
**Note:** It's not safe to call ``json_object_deln(object, key, key_len)``
during iteration. If you need to, use
:func:`json_object_keylen_foreach_safe` instead.
.. versionadded:: 2.14
.. function:: void json_object_keylen_foreach_safe(object, tmp, key, key_len, value)
Like :func:`json_object_keylen_foreach()`, but it's safe to call
``json_object_deln(object, key, key_len)`` during iteration.
You need to pass an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
.. versionadded:: 2.14
The following functions can be used to iterate through all key-value
pairs in an object. The items are returned in the order they were
@ -732,6 +872,12 @@ inserted to the object.
Extract the associated key from *iter*.
.. function:: size_t json_object_iter_key_len(void *iter)
Extract the associated key length from *iter*.
.. versionadded:: 2.14
.. function:: json_t *json_object_iter_value(void *iter)
.. refcounting:: borrow
@ -787,8 +933,7 @@ inserted to the object.
:func:`json_object()`, either explicit or implicit. If this
function is not called by the user, the first call to
:func:`json_object()` (either explicit or implicit) seeds the hash
function. See :ref:`portability-thread-safety` for notes on thread
safety.
function. See :ref:`thread-safety` for notes on thread safety.
If repeatable results are required, for e.g. unit tests, the hash
function can be "unrandomized" by calling :func:`json_object_seed`
@ -813,6 +958,9 @@ this struct.
The error message (in UTF-8), or an empty string if a message is
not available.
The last byte of this array contains a numeric error code. Use
:func:`json_error_code()` to extract this code.
.. member:: char source[]
Source of the error. This can be (a part of) the file name or a
@ -855,11 +1003,102 @@ success. See :ref:`apiref-decoding` for more info.
All functions also accept *NULL* as the :type:`json_error_t` pointer,
in which case no error information is returned to the caller.
.. c:enum:: json_error_code
An enumeration containing numeric error codes. The following errors are
currently defined:
``json_error_unknown``
Unknown error. This should only be returned for non-errorneous
:type:`json_error_t` structures.
``json_error_out_of_memory``
The library couldnt allocate any heap memory.
``json_error_stack_overflow``
Nesting too deep.
``json_error_cannot_open_file``
Couldnt open input file.
``json_error_invalid_argument``
A function argument was invalid.
``json_error_invalid_utf8``
The input string isnt valid UTF-8.
``json_error_premature_end_of_input``
The input ended in the middle of a JSON value.
``json_error_end_of_input_expected``
There was some text after the end of a JSON value. See the
``JSON_DISABLE_EOF_CHECK`` flag.
``json_error_invalid_syntax``
JSON syntax error.
``json_error_invalid_format``
Invalid format string for packing or unpacking.
``json_error_wrong_type``
When packing or unpacking, the actual type of a value differed from the
one specified in the format string.
``json_error_null_character``
A null character was detected in a JSON string. See the
``JSON_ALLOW_NUL`` flag.
``json_error_null_value``
When packing or unpacking, some key or value was ``NULL``.
``json_error_null_byte_in_key``
An object key would contain a null byte. Jansson cant represent such
keys; see :ref:`rfc-conformance`.
``json_error_duplicate_key``
Duplicate key in object. See the ``JSON_REJECT_DUPLICATES`` flag.
``json_error_numeric_overflow``
When converting a JSON number to a C numeric type, a numeric overflow
was detected.
``json_error_item_not_found``
Key in object not found.
``json_error_index_out_of_range``
Array index is out of range.
.. versionadded:: 2.11
.. function:: enum json_error_code json_error_code(const json_error_t *error)
Returns the error code embedded in ``error->text``.
.. versionadded:: 2.11
Encoding
========
This sections describes the functions that can be used to encode
This section describes the functions that can be used to encode
values to JSON. By default, only objects and arrays can be encoded
directly, since they are the only valid *root* values of a JSON text.
To encode any JSON value, use the ``JSON_ENCODE_ANY`` flag (see
@ -953,7 +1192,9 @@ These functions output UTF-8:
Returns the JSON representation of *json* as a string, or *NULL* on
error. *flags* is described above. The return value must be freed
by the caller using :func:`free()`.
by the caller using :func:`free()`. Note that if you have called
:func:`json_set_alloc_funcs()` to override :func:`free()`, you should
call your custom free function instead to free the return value.
.. function:: size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
@ -1019,6 +1260,10 @@ These functions output UTF-8:
the length of the buffer, and *data* is the corresponding
:func:`json_dump_callback()` argument passed through.
*buffer* is guaranteed to be a valid UTF-8 string (i.e. multi-byte
code unit sequences are preserved). *buffer* never contains
embedded null bytes.
On error, the function should return -1 to stop the encoding
process. On success, it should return 0.
@ -1038,7 +1283,7 @@ These functions output UTF-8:
Decoding
========
This sections describes the functions that can be used to decode JSON
This section describes the functions that can be used to decode JSON
text to the Jansson representation of JSON data. The JSON
specification requires that a JSON text is either a serialized array
or object, and this requirement is also enforced with the following
@ -1189,7 +1434,11 @@ If no error or position information is needed, you can pass *NULL*.
It is important to note that this function can only succeed on stream
file descriptors (such as SOCK_STREAM). Using this function on a
non-stream file descriptor will result in undefined behavior. For
non-stream file descriptors, see instead :func:`json_loadb()`.
non-stream file descriptors, see instead :func:`json_loadb()`. In
addition, please note that this function cannot be used on non-blocking
file descriptors (such as a non-blocking socket). Using this function
on non-blocking file descriptors has a high risk of data loss because
it does not support resuming.
This function requires POSIX and fails on all non-POSIX systems.
@ -1214,11 +1463,19 @@ If no error or position information is needed, you can pass *NULL*.
*buffer* points to a buffer of *buflen* bytes, and *data* is the
corresponding :func:`json_load_callback()` argument passed through.
On success, the function should return the number of bytes read; a
returned value of 0 indicates that no data was read and that the
end of file has been reached. On error, the function should return
On success, the function should write at most *buflen* bytes to
*buffer*, and return the number of bytes written; a returned value
of 0 indicates that no data was produced and that the end of file
has been reached. On error, the function should return
``(size_t)-1`` to abort the decoding process.
In UTF-8, some code points are encoded as multi-byte sequences. The
callback function doesn't need to worry about this, as Jansson
handles it at a higher level. For example, you can safely read a
fixed number of bytes from a network connection without having to
care about code unit sequences broken apart by the chunk
boundaries.
.. versionadded:: 2.4
.. function:: json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error)
@ -1269,6 +1526,14 @@ arguments.
.. versionadded:: 2.8
``s*`` (string) [const char \*]
Like ``s``, but if the argument is *NULL*, do not output any value.
This format can only be used inside an object or an array. If used
inside an object, the corresponding key is additionally suppressed
when the value is omitted. See below for an example.
.. versionadded:: 2.11
``s#`` (string) [const char \*, int]
Convert a UTF-8 buffer of a given length to a JSON string.
@ -1300,17 +1565,17 @@ arguments.
Output a JSON null value. No argument is consumed.
``b`` (boolean) [int]
Convert a C :type:`int` to JSON boolean value. Zero is converted
Convert a C ``int`` to JSON boolean value. Zero is converted
to ``false`` and non-zero to ``true``.
``i`` (integer) [int]
Convert a C :type:`int` to JSON integer.
Convert a C ``int`` to JSON integer.
``I`` (integer) [json_int_t]
Convert a C :type:`json_int_t` to JSON integer.
``f`` (real) [double]
Convert a C :type:`double` to JSON real.
Convert a C ``double`` to JSON real.
``o`` (any value) [json_t \*]
Output any given JSON value as-is. If the value is added to an
@ -1324,11 +1589,20 @@ arguments.
yourself.
``o?``, ``O?`` (any value) [json_t \*]
Like ``o`` and ``O?``, respectively, but if the argument is
Like ``o`` and ``O``, respectively, but if the argument is
*NULL*, output a JSON null value.
.. versionadded:: 2.8
``o*``, ``O*`` (any value) [json_t \*]
Like ``o`` and ``O``, respectively, but if the argument is
*NULL*, do not output any value. This format can only be used
inside an object or an array. If used inside an object, the
corresponding key is additionally suppressed. See below for an
example.
.. versionadded:: 2.11
``[fmt]`` (array)
Build an array with contents from the inner format string. ``fmt``
may contain objects and arrays, i.e. recursive value building is
@ -1387,6 +1661,10 @@ More examples::
/* Concatenate strings together to build the JSON string "foobarbaz" */
json_pack("s++", "foo", "bar", "baz");
/* Create an empty object or array when optional members are missing */
json_pack("{s:s*,s:o*,s:O*}", "foo", NULL, "bar", NULL, "baz", NULL);
json_pack("[s*,o*,O*]", NULL, NULL, NULL);
.. _apiref-unpack:
@ -1424,26 +1702,29 @@ type whose address should be passed.
Expect a JSON null value. Nothing is extracted.
``b`` (boolean) [int]
Convert a JSON boolean value to a C :type:`int`, so that ``true``
Convert a JSON boolean value to a C ``int``, so that ``true``
is converted to 1 and ``false`` to 0.
``i`` (integer) [int]
Convert a JSON integer to C :type:`int`.
Convert a JSON integer to C ``int``.
``I`` (integer) [json_int_t]
Convert a JSON integer to C :type:`json_int_t`.
``f`` (real) [double]
Convert a JSON real to C :type:`double`.
Convert a JSON real to C ``double``.
``F`` (integer or real) [double]
Convert a JSON number (integer or real) to C :type:`double`.
Convert a JSON number (integer or real) to C ``double``.
``o`` (any value) [json_t \*]
Store a JSON value with no conversion to a :type:`json_t` pointer.
``O`` (any value) [json_t \*]
Like ``O``, but the JSON value's reference count is incremented.
Like ``o``, but the JSON value's reference count is incremented.
Storage pointers should be initialized NULL before using unpack.
The caller is responsible for releasing all references incremented
by unpack, even when an error occurs.
``[fmt]`` (array)
Convert each item in the JSON array according to the inner format
@ -1454,7 +1735,7 @@ type whose address should be passed.
Convert each item in the JSON object according to the inner format
string ``fmt``. The first, third, etc. format specifier represent
a key, and must be ``s``. The corresponding argument to unpack
functions is read as the object key. The second fourth, etc.
functions is read as the object key. The second, fourth, etc.
format specifier represent a value and is written to the address
given as the corresponding argument. **Note** that every other
argument is read from and every other is written to.
@ -1705,3 +1986,79 @@ memory, see
http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html.
The page also explains the :func:`guaranteed_memset()` function used
in the example and gives a sample implementation for it.
.. _fixed_length_keys:
Fixed-Length keys
=================
The Jansson API allows work with fixed-length keys. This can be useful in the following cases:
* The key is contained inside a buffer and is not null-terminated. In this case creating a new temporary buffer is not needed.
* The key contains U+0000 inside it.
List of API for fixed-length keys:
* :c:func:`json_object_getn`
* :c:func:`json_object_setn`
* :c:func:`json_object_setn_nocheck`
* :c:func:`json_object_setn_new`
* :c:func:`json_object_setn_new_nocheck`
* :c:func:`json_object_deln`
* :c:func:`json_object_iter_key_len`
* :c:func:`json_object_keylen_foreach`
* :c:func:`json_object_keylen_foreach_safe`
**Examples:**
Try to write a new function to get :c:struct:`json_t` by path separated by ``.``
This requires:
* string iterator (no need to modify the input for better performance)
* API for working with fixed-size keys
The iterator::
struct string {
const char *string;
size_t length;
};
size_t string_try_next(struct string *str, const char *delimiter) {
str->string += strspn(str->string, delimiter);
str->length = strcspn(str->string, delimiter);
return str->length;
}
#define string_foreach(_string, _delimiter) \
for (; string_try_next(&(_string), _delimiter); (_string).string += (_string).length)
The function::
json_t *json_object_get_by_path(json_t *object, const char *path) {
struct string str;
json_t *out = object;
str.string = path;
string_foreach(str, ".") {
out = json_object_getn(out, str.string, str.length);
if (out == NULL)
return NULL;
}
return out;
}
And usage::
int main(void) {
json_t *obj = json_pack("{s:{s:{s:b}}}", "a", "b", "c", 1);
json_t *c = json_object_get_by_path(obj, "a.b.c");
assert(json_is_true(c));
json_decref(obj);
}

View file

@ -41,14 +41,14 @@ master_doc = 'index'
# General information about the project.
project = u'Jansson'
copyright = u'2009-2016, 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.10'
version = '2.14.1'
# 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
@ -110,7 +109,7 @@ 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

View file

@ -24,8 +24,8 @@
"""
from docutils import nodes
from docutils.parsers.rst import Directive
class refcounting(nodes.emphasis): pass
def visit(self, node):
self.visit_emphasis(node)
@ -40,16 +40,25 @@ 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,
@ -57,4 +66,4 @@ def setup(app):
latex=(visit, depart),
text=(visit, depart),
man=(visit, depart))
app.add_directive('refcounting', refcounting_directive, 0, (1, 0, 0))
app.add_directive('refcounting', refcounting_directive)

View file

@ -84,6 +84,11 @@ Generating make files on unix:
cd build
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
@ -101,7 +106,23 @@ 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.
@ -121,6 +142,21 @@ 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,

View file

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

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

View file

@ -22,15 +22,15 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <stdio.h>
#include <stdlib.h>
#include <jansson.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(int count);
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);
@ -40,49 +40,47 @@ 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(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));
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(' '); }
for (i = 0; i < indent; i++) {
putchar(' ');
}
}
const char *json_plural(int count) {
return count == 1 ? "" : "s";
}
const char *json_plural(size_t count) { return count == 1 ? "" : "s"; }
void print_json_object(json_t *element, int indent) {
size_t size;
@ -92,13 +90,12 @@ void print_json_object(json_t *element, int indent) {
print_json_indent(indent);
size = json_object_size(element);
printf("JSON Object of %ld pair%s:\n", size, json_plural(size));
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) {
@ -106,7 +103,7 @@ void print_json_array(json_t *element, int indent) {
size_t size = json_array_size(element);
print_json_indent(indent);
printf("JSON Array of %ld element%s:\n", size, json_plural(size));
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);
}

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
git ls-files | grep '\.[ch]$' | xargs clang-format -i

30
scripts/clang-format-check Executable file
View file

@ -0,0 +1,30 @@
#!/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
echo "Checking $path"
$CLANG_FORMAT $path > $path.formatted
in=$(cat $path)
out=$(cat $path.formatted)
if [ "$in" != "$out" ]; then
diff -u $path $path.formatted
errors=1
fi
rm $path.formatted
done
if [ $errors -ne 0 ]; then
echo "Formatting errors detected, run ./scripts/clang-format to fix!"
exit 1
fi

View file

@ -1,4 +1,4 @@
EXTRA_DIST = jansson.def
EXTRA_DIST = jansson.def dtoa.c
include_HEADERS = jansson.h
nodist_include_HEADERS = jansson_config.h
@ -20,8 +20,16 @@ libjansson_la_SOURCES = \
strconv.c \
utf.c \
utf.h \
value.c
value.c \
version.c
if DTOA_ENABLED
libjansson_la_SOURCES += dtoa.c
endif
libjansson_la_LDFLAGS = \
-no-undefined \
-export-symbols-regex '^json_' \
-version-info 14:0:10
-export-symbols-regex '^json_|^jansson_' \
-version-info 18:1:14 \
@JSON_SYMVER_LDFLAGS@ \
@JSON_BSYMBOLIC_LDFLAGS@

6265
src/dtoa.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,10 +11,10 @@
#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
@ -23,11 +23,11 @@
#include "strbuffer.h"
#include "utf.h"
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
#define MAX_INTEGER_STR_LENGTH 25
#define MAX_REAL_STR_LENGTH 25
#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 buffer {
const size_t size;
@ -35,35 +35,31 @@ struct buffer {
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_buffer(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)
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)
{
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)
{
int *dest = (int *)data;
static int dump_to_fd(const char *buffer, size_t size, void *data) {
#ifdef HAVE_UNISTD_H
if(write(*dest, buffer, size) == (ssize_t)size)
int *dest = (int *)data;
if (write(*dest, buffer, size) == (ssize_t)size)
return 0;
#endif
return -1;
@ -72,108 +68,116 @@ static int dump_to_fd(const char *buffer, size_t size, void *data)
/* 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)
{
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;
while(n_spaces > 0)
{
int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
while (n_spaces > 0) {
int cur_n =
n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
if(dump(whitespace, cur_n, data))
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)
{
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);
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first,
(unsigned int)last);
length = 12;
}
@ -182,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;
@ -191,22 +195,33 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
return dump("\"", 1, data);
}
static int compare_keys(const void *key1, const void *key2)
{
return strcmp(*(const char **)key1, *(const char **)key2);
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 do_dump(const json_t *json, size_t flags, int depth,
json_dump_callback_t dump, void *data)
{
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;
flags &= ~JSON_EMBED;
if(!json)
if (!json)
return -1;
switch(json_typeof(json)) {
switch (json_typeof(json)) {
case JSON_NULL:
return dump("null", 4, data);
@ -216,216 +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:
{
case JSON_ARRAY: {
size_t n;
size_t i;
json_array_t *array;
/* Space for "0x", double the sizeof a pointer for the hex and a
* terminator. */
char key[2 + (sizeof(json) * 2) + 1];
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(!embed && dump("[", 1, data))
goto array_error;
if(n == 0) {
array->visited = 0;
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;
hashtable_del(parents, key, key_len);
return embed ? 0 : dump("]", 1, data);
array_error:
array->visited = 0;
return -1;
}
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(!embed && dump("{", 1, data))
goto object_error;
if(!iter) {
object->visited = 0;
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)
{
const char **keys;
if (flags & JSON_SORT_KEYS) {
struct key_len *keys;
size_t size, i;
size = json_object_size(json);
keys = jsonp_malloc(size * sizeof(const char *));
if(!keys)
goto object_error;
keys = jsonp_malloc(size * sizeof(struct key_len));
if (!keys)
return -1;
i = 0;
while(iter)
{
keys[i] = 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);
qsort(keys, size, sizeof(const char *), compare_keys);
qsort(keys, size, sizeof(struct key_len), compare_keys);
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];
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;
hashtable_del(parents, loop_key, loop_key_len);
return embed ? 0 : dump("}", 1, data);
object_error:
object->visited = 0;
return -1;
}
default:
@ -434,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));
@ -451,46 +439,52 @@ char *json_dumps(const json_t *json, size_t flags)
return result;
}
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
{
struct buffer buf = { size, 0, buffer };
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))
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)
{
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_dumpfd(const json_t *json, int output, 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 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,55 +1,50 @@
#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)
if (length < JSON_ERROR_SOURCE_LENGTH)
strncpy(error->source, source, length + 1);
else {
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
strncpy(error->source, "...", 3);
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;
}
@ -58,6 +53,7 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
error->column = column;
error->position = (int)position;
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap);
error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0';
error->text[JSON_ERROR_TEXT_LENGTH - 1] = code;
}

View file

@ -5,20 +5,20 @@
* 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
@ -33,67 +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 ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_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;
@ -103,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;
@ -113,17 +102,17 @@ 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);
@ -136,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);
@ -150,8 +137,7 @@ 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, new_order;
@ -161,23 +147,21 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
new_size = hashsize(new_order);
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if(!new_buckets)
if (!new_buckets)
return -1;
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;
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;
@ -187,77 +171,83 @@ 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 = 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, 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;
strncpy(pair->key, key, len + 1);
pair->value = value;
list_init(&pair->list);
list_init(&pair->ordered_list);
insert_to_bucket(hashtable, bucket, &pair->list);
list_insert(&hashtable->ordered_list, &pair->ordered_list);
@ -267,38 +257,33 @@ int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
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);
@ -306,49 +291,48 @@ void hashtable_clear(hashtable_t *hashtable)
hashtable->size = 0;
}
void *hashtable_iter(hashtable_t *hashtable)
{
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->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->ordered_list)
if (list->next == &hashtable->ordered_list)
return NULL;
return list->next;
}
void *hashtable_iter_key(void *iter)
{
void *hashtable_iter_key(void *iter) {
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->key;
}
void *hashtable_iter_value(void *iter)
{
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 = ordered_list_to_pair((list_t *)iter);
return pair->value;
}
void hashtable_iter_set(void *iter, json_t *value)
{
void hashtable_iter_set(void *iter, json_t *value) {
pair_t *pair = ordered_list_to_pair((list_t *)iter);
json_decref(pair->value);

View file

@ -8,8 +8,8 @@
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <stdlib.h>
#include "jansson.h"
#include <stdlib.h>
struct hashtable_list {
struct hashtable_list *prev;
@ -24,6 +24,7 @@ struct hashtable_pair {
struct hashtable_list ordered_list;
size_t hash;
json_t *value;
size_t key_len;
char key[1];
};
@ -35,16 +36,14 @@ 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_) \
#define hashtable_key_to_iter(key_) \
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
/**
* hashtable_init - Initialize a hashtable object
*
@ -55,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
@ -71,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
*
@ -81,27 +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, 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
@ -134,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
@ -158,6 +161,13 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
*/
void *hashtable_iter_key(void *iter);
/**
* hashtable_iter_key_len - Retrieve the key length pointed by an iterator
*
* @iter: The iterator
*/
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,7 +165,7 @@ 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)
@ -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,17 +34,23 @@ 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
@ -70,4 +78,6 @@ EXPORTS
json_vunpack_ex
json_set_alloc_funcs
json_get_alloc_funcs
jansson_version_str
jansson_version_cmp

View file

@ -8,9 +8,9 @@
#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"
@ -20,19 +20,30 @@ extern "C" {
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 10
#define JANSSON_MICRO_VERSION 0
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 14
#define JANSSON_MICRO_VERSION 1
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.10"
#define JANSSON_VERSION "2.14.1"
/* 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,47 +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) {
static JSON_INLINE void json_decrefp(json_t **json) {
if (json) {
json_decref(*json);
*json = NULL;
*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;
@ -139,64 +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_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(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)))
#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_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)))
static JSON_INLINE
int json_object_set(json_t *object, const char *key, json_t *value)
{
#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);
@ -204,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));
}
@ -237,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 */
@ -270,35 +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_loadfd(int 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_EMBED 0x10000
#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 */
@ -308,6 +407,11 @@ 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
}
#endif

View file

@ -32,9 +32,13 @@
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
/* 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. */

View file

@ -8,18 +8,18 @@
#ifndef JANSSON_PRIVATE_H
#define JANSSON_PRIVATE_H
#include "jansson_private_config.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
@ -28,14 +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;
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,36 +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 */
#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
#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

@ -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,14 +48,12 @@ 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)
{
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)

File diff suppressed because it is too large Load diff

View file

@ -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,47 +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_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);
@ -99,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

@ -8,15 +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);

View file

@ -1,83 +1,58 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __MINGW32__
#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */
#endif
#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
#include <jansson_private_config.h>
#endif
#ifdef __MINGW32__
#define strtod __strtod
#endif
#if JSON_HAVE_LOCALECONV
#include <locale.h>
/*
- This code assumes that the decimal separator is exactly one
character.
- If setlocale() is called by another thread between the call to
localeconv() and the call to sprintf() or strtod(), the result may
be wrong. setlocale() is not thread-safe and should not be used
this way. Multi-threaded programs should use uselocale() instead.
get_decimal_point() and the call to sprintf() or strtod(), the
result may be wrong. setlocale() is not thread-safe and should
not be used this way. Multi-threaded programs should use
uselocale() instead.
*/
static char get_decimal_point() {
char buf[3];
sprintf(buf, "%#.0f", 1.0); // "1." in the current locale
return buf[1];
}
static void to_locale(strbuffer_t *strbuffer)
{
const char *point;
static void to_locale(strbuffer_t *strbuffer) {
char point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
point = get_decimal_point();
if (point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
*pos = *point;
if (pos)
*pos = point;
}
static void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
#endif
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
{
int jsonp_strtod(strbuffer_t *strbuffer, double *out) {
double value;
char *end;
#if JSON_HAVE_LOCALECONV
to_locale(strbuffer);
#endif
errno = 0;
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;
}
@ -86,8 +61,128 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
return 0;
}
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
{
#if DTOA_ENABLED
/* see dtoa.c */
char *dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve,
char *buf, size_t blen);
int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
/* adapted from `format_float_short()` in
* https://github.com/python/cpython/blob/2cf18a44303b6d84faa8ecffaecc427b53ae121e/Python/pystrtod.c#L969
*/
char digits[25];
char *digits_end;
int mode = precision == 0 ? 0 : 2;
int decpt, sign, exp_len, exp = 0, use_exp = 0;
int digits_len, vdigits_start, vdigits_end;
char *p;
if (dtoa_r(value, mode, precision, &decpt, &sign, &digits_end, digits, 25) == NULL) {
// digits is too short => should not happen
return -1;
}
digits_len = digits_end - digits;
if (decpt <= -4 || decpt > 16) {
use_exp = 1;
exp = decpt - 1;
decpt = 1;
}
vdigits_start = decpt <= 0 ? decpt - 1 : 0;
vdigits_end = digits_len;
if (!use_exp) {
/* decpt + 1 to add ".0" if value is an integer */
vdigits_end = vdigits_end > decpt ? vdigits_end : decpt + 1;
} else {
vdigits_end = vdigits_end > decpt ? vdigits_end : decpt;
}
if (
/* sign, decimal point and trailing 0 byte */
(size_t)(3 +
/* total digit count (including zero padding on both sides) */
(vdigits_end - vdigits_start) +
/* exponent "e+100", max 3 numerical digits */
(use_exp ? 5 : 0)) > size) {
/* buffer is too short */
return -1;
}
p = buffer;
if (sign == 1) {
*p++ = '-';
}
/* note that exactly one of the three 'if' conditions is true,
so we include exactly one decimal point */
/* Zero padding on left of digit string */
if (decpt <= 0) {
memset(p, '0', decpt - vdigits_start);
p += decpt - vdigits_start;
*p++ = '.';
memset(p, '0', 0 - decpt);
p += 0 - decpt;
} else {
memset(p, '0', 0 - vdigits_start);
p += 0 - vdigits_start;
}
/* Digits, with included decimal point */
if (0 < decpt && decpt <= digits_len) {
strncpy(p, digits, decpt - 0);
p += decpt - 0;
*p++ = '.';
strncpy(p, digits + decpt, digits_len - decpt);
p += digits_len - decpt;
} else {
strncpy(p, digits, digits_len);
p += digits_len;
}
/* And zeros on the right */
if (digits_len < decpt) {
memset(p, '0', decpt - digits_len);
p += decpt - digits_len;
*p++ = '.';
memset(p, '0', vdigits_end - decpt);
p += vdigits_end - decpt;
} else {
memset(p, '0', vdigits_end - digits_len);
p += vdigits_end - digits_len;
}
if (p[-1] == '.')
p--;
if (use_exp) {
*p++ = 'e';
exp_len = sprintf(p, "%d", exp);
p += exp_len;
}
*p = '\0';
return (int)(p - buffer);
}
#else /* DTOA_ENABLED == 0 */
static void from_locale(char *buffer) {
char point;
char *pos;
point = get_decimal_point();
if (point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, point);
if (pos)
*pos = '.';
}
int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
int ret;
char *start, *end;
size_t length;
@ -96,23 +191,19 @@ 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
from_locale(buffer);
#endif
/* 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;
}
@ -125,17 +216,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);
}
@ -143,3 +234,4 @@ int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
return (int)length;
}
#endif

110
src/utf.c
View file

@ -5,107 +5,85 @@
* 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

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

10
test/.gitignore vendored
View file

@ -1,21 +1,21 @@
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_fixed_size
suites/api/test_load
suites/api/test_load_callback
suites/api/test_loadb
suites/api/test_memory_funcs
suites/api/test_number
suites/api/test_object
suites/api/test_pack
suites/api/test_simple
suites/api/test_sprintf
suites/api/test_unpack
suites/api/test_load_callback
run-suites.log
run-suites.trs
test-suite.log
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

@ -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;
@ -36,7 +35,6 @@ struct config {
int ensure_ascii;
int sort_keys;
int strip;
int use_env;
int have_hashseed;
int hashseed;
int precision;
@ -47,8 +45,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 +61,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,15 +78,12 @@ static char *loadfile(FILE *file)
return buf;
}
static void read_conf(FILE *conffile)
{
static void read_conf(FILE *conffile) {
char *buffer, *line, *val;
conf.have_hashseed = 0;
buffer = loadfile(conffile);
for (line = strtok(buffer, "\r\n"); line; line = strtok(NULL, "\r\n")) {
if (!strncmp(line, "export ", 7))
continue;
val = strchr(line, '=');
if (!val) {
printf("invalid configuration line\n");
@ -116,17 +108,13 @@ static void read_conf(FILE *conffile)
if (!strcmp(line, "HASHSEED")) {
conf.have_hashseed = 1;
conf.hashseed = atoi(val);
} else {
conf.have_hashseed = 0;
}
}
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;
@ -146,18 +134,23 @@ static int cmpfile(const char *str, const char *path, const char *fname)
}
buffer = loadfile(file);
if (strcmp(buffer, str) != 0)
if (strcmp(buffer, str) != 0) {
fprintf(stderr, "=== Expected %s ===\n", fname);
fprintf(stderr, "%s\n", buffer);
fprintf(stderr, "=== Actual %s ===\n", fname);
fprintf(stderr, "%s\n", str);
ret = 1;
else
} else {
ret = 0;
}
free(buffer);
fclose(file);
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 +193,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 +208,14 @@ 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,121 +230,13 @@ int use_conf(char *test_path)
return ret;
}
static int getenv_int(const char *name)
{
char *value, *end;
long result;
value = getenv(name);
if(!value)
return 0;
result = strtol(value, &end, 10);
if(*end != '\0')
return 0;
return (int)result;
}
int use_env()
{
int indent, precision;
size_t flags = 0;
json_t *json;
json_error_t error;
#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
indent = getenv_int("JSON_INDENT");
if(indent < 0 || indent > 31) {
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
return 2;
}
if(indent > 0)
flags |= JSON_INDENT(indent);
if(getenv_int("JSON_COMPACT") > 0)
flags |= JSON_COMPACT;
if(getenv_int("JSON_ENSURE_ASCII"))
flags |= JSON_ENSURE_ASCII;
if(getenv_int("JSON_PRESERVE_ORDER"))
flags |= JSON_PRESERVE_ORDER;
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);
return 2;
}
if(getenv("HASHSEED"))
json_object_seed(getenv_int("HASHSEED"));
if(precision > 0)
flags |= JSON_REAL_PRECISION(precision);
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) {
size_t count;
size = (size == 0 ? 128 : size * 2);
buf_ck = realloc(buffer, size);
if(!buf_ck) {
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
free(buffer);
return 1;
}
buffer = buf_ck;
count = fread(buffer + used, 1, size - used, stdin);
if(count < size - used) {
buffer[used + count] = '\0';
break;
}
used += count;
}
json = json_loads(strip(buffer), 0, &error);
free(buffer);
}
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);
return 1;
}
json_dumpf(json, stdout, flags);
json_decref(json);
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;
@ -362,24 +245,17 @@ int main(int argc, char *argv[])
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--strip"))
conf.strip = 1;
else if (!strcmp(argv[i], "--env"))
conf.use_env = 1;
else
test_path = argv[i];
}
if (conf.use_env)
return use_env();
else
{
if (!test_path)
goto usage;
return use_conf(test_path);
if (!test_path) {
goto usage;
}
return use_conf(test_path);
usage:
fprintf(stderr, "argc =%d\n", argc);
fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]);
fprintf(stderr, "usage: %s [--strip] test_dir\n", argv[0]);
return 2;
}

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

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

@ -5,11 +5,10 @@
* 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

@ -5,21 +5,20 @@
* 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,8 +221,7 @@ 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]}";
@ -239,21 +232,20 @@ static void test_copy_object(void)
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;
@ -261,7 +253,7 @@ static void test_copy_object(void)
value1 = json_object_iter_value(iter);
value2 = json_object_get(copy, key);
if(value1 != value2)
if (value1 != value2)
fail("copying an object modifies its items");
if (strcmp(key, keys[i]) != 0)
@ -275,8 +267,7 @@ static void test_copy_object(void)
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]}";
@ -287,21 +278,20 @@ static void test_deep_copy_object(void)
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;
@ -309,7 +299,7 @@ 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)
@ -323,12 +313,63 @@ static void test_deep_copy_object(void)
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

@ -13,40 +13,40 @@
#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_dumpb(NULL, NULL, 0, JSON_ENCODE_ANY) != 0)
fail("json_dumps didn't fail for NULL");
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)
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)
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;
@ -54,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>}}}
@ -100,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);
@ -118,21 +117,20 @@ 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 */
@ -140,40 +138,37 @@ static void encode_other_than_array_or_object()
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, NULL, 0) == 0)
if (json_dumpf(json, NULL, 0) == 0)
fail("json_dumpf encoded a string!");
if(json_dumpfd(json, -1, 0) == 0)
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, NULL, 0) == 0)
if (json_dumpf(json, NULL, 0) == 0)
fail("json_dumpf encoded an integer!");
if(json_dumpfd(json, -1, 0) == 0)
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;
@ -183,35 +178,34 @@ 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 dump_file()
{
static void dump_file() {
json_t *json;
int result;
@ -228,8 +222,7 @@ static void dump_file()
remove("json_dump_file.json");
}
static void dumpb()
{
static void dumpb() {
char buf[2];
json_t *obj;
size_t size;
@ -237,31 +230,30 @@ static void dumpb()
obj = json_object();
size = json_dumpb(obj, buf, sizeof(buf), 0);
if(size != 2 || strncmp(buf, "{}", 2))
fail("json_dumpb failed");
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");
if (size != 13)
fail("json_dumpb size check failed");
json_decref(obj);
}
static void dumpfd()
{
static void dumpfd() {
#ifdef HAVE_UNISTD_H
int fds[2] = {-1, -1};
json_t *a, *b;
if(pipe(fds))
if (pipe(fds))
fail("pipe() failed");
a = json_pack("{s:s}", "foo", "bar");
if(json_dumpfd(a, fds[1], 0))
if (json_dumpfd(a, fds[1], 0))
fail("json_dumpfd() failed");
close(fds[1]);
@ -278,19 +270,13 @@ static void dumpfd()
#endif
}
static void embed()
{
static const char *plains[] = {
"{\"bar\":[],\"foo\":{}}",
"[[],{}]",
"{}",
"[]",
NULL
};
static void embed() {
static const char *plains[] = {"{\"bar\":[],\"foo\":{}}", "[[],{}]", "{}", "[]",
NULL};
size_t i;
for(i = 0; plains[i]; i++) {
for (i = 0; plains[i]; i++) {
const char *plain = plains[i];
json_t *parse = NULL;
char *embed = NULL;
@ -300,19 +286,18 @@ static void embed()
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);
esize =
json_dumpb(parse, embed, psize, JSON_COMPACT | JSON_SORT_KEYS | JSON_EMBED);
json_decref(parse);
if(esize != psize)
if (esize != psize)
fail("json_dumpb(JSON_EMBED) returned an invalid size");
if(strncmp(plain + 1, embed, esize) != 0)
if (strncmp(plain + 1, embed, esize) != 0)
fail("json_dumps(JSON_EMBED) returned an invalid value");
free(embed);
}
}
static void run_tests()
{
static void run_tests() {
encode_null();
encode_twice();
circular_references();

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

@ -5,38 +5,37 @@
* 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

@ -5,78 +5,82 @@
* 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 very_long_file_name() {
json_t *json;
json_error_t error;
json = json_load_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, &error);
if(json)
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)
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 reject_duplicates()
{
static void reject_duplicates() {
json_error_t error;
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &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);
check_error(json_error_duplicate_key, "duplicate object key near '\"foo\"'",
"<string>", 1, 16, 16);
}
static void disable_eof_check()
{
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;
@ -101,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;
@ -123,8 +126,7 @@ 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);
@ -137,34 +139,32 @@ static void decode_int_as_real()
big[310] = '\0';
json = json_loads(big, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
if (json || strcmp(error.text, "real number overflow") != 0)
if (json || strcmp(error.text, "real number overflow") != 0 ||
json_error_code(&error) != json_error_numeric_overflow)
fail("json_load decode int as real failed - expected overflow");
json_decref(json);
}
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;
@ -189,25 +189,42 @@ static void load_wrong_args()
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();
@ -217,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

@ -5,19 +5,18 @@
* 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,5 +1,5 @@
#include <string.h>
#include <jansson.h>
#include <string.h>
#include "util.h"
@ -8,29 +8,21 @@ static int free_called = 0;
static size_t malloc_used = 0;
/* helpers */
static void create_and_free_complex_object()
{
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 create_and_free_object_with_oom()
{
static void create_and_free_object_with_oom() {
int i;
char key[4];
json_t *obj = json_object();
for (i = 0; i < 10; i++)
{
for (i = 0; i < 10; i++) {
snprintf(key, sizeof key, "%d", i);
json_object_set_new(obj, key, json_integer(i));
}
@ -38,20 +30,17 @@ static void create_and_free_object_with_oom()
json_decref(obj);
}
static void *my_malloc(size_t size)
{
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;
@ -59,14 +48,11 @@ static void test_simple()
json_get_alloc_funcs(&mfunc, &ffunc);
create_and_free_complex_object();
if (malloc_called != 1 || free_called != 1
|| mfunc != my_malloc || ffunc != my_free)
if (malloc_called != 1 || free_called != 1 || mfunc != my_malloc || ffunc != my_free)
fail("Custom allocation failed");
}
static void *oom_malloc(size_t size)
{
static void *oom_malloc(size_t size) {
if (malloc_used + size > 800)
return NULL;
@ -74,14 +60,12 @@ static void *oom_malloc(size_t size)
return malloc(size);
}
static void oom_free(void *ptr)
{
static void oom_free(void *ptr) {
free_called++;
free(ptr);
}
static void test_oom()
{
static void test_oom() {
free_called = 0;
json_set_alloc_funcs(oom_malloc, oom_free);
create_and_free_object_with_oom();
@ -90,41 +74,42 @@ static void test_oom()
fail("Allocation with OOM failed");
}
/*
Test the secure memory functions code given in the API reference
documentation, but by using plain memset instead of
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

@ -5,9 +5,9 @@
* 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>
#ifdef INFINITY
// This test triggers "warning C4756: overflow in constant arithmetic"
@ -15,20 +15,19 @@
// (This can only be done on function level so we keep these tests separate)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning (disable: 4756)
#pragma warning(disable : 4756)
#endif
static void test_inifity()
{
static void test_inifity() {
json_t *real = json_real(INFINITY);
if (real != NULL)
fail("could construct a real from Inf");
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");
fail("could set a real to Inf");
if (json_real_value(real) != 1.0)
fail("real value changed unexpectedly");
fail("real value changed unexpectedly");
json_decref(real);
#ifdef _MSC_VER
@ -37,8 +36,41 @@ static void test_inifity()
}
#endif // INFINITY
static void run_tests()
{
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;
@ -46,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);
@ -71,14 +103,14 @@ 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);
@ -87,4 +119,5 @@ static void run_tests()
#ifdef INFINITY
test_inifity();
#endif
test_bad_args();
}

View file

@ -5,43 +5,39 @@
* 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,97 +46,104 @@ 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_set_many_keys()
{
static void test_set_many_keys() {
json_t *object, *value;
const char *keys = "abcdefghijklmnopqrstuvwxyz";
char buf[2];
@ -165,68 +168,181 @@ static void test_set_many_keys()
json_decref(value);
}
static void test_conditional_updates()
{
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 */
@ -235,72 +351,69 @@ 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()
{
static void test_iterators() {
json_t *object, *foo, *bar, *baz;
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 || !baz)
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");
if (strcmp(json_object_iter_key(iter), "a") != 0)
fail("iterating doesn't yield keys in order");
@ -308,7 +421,7 @@ static void test_iterators()
fail("iterating doesn't yield values in order");
iter = json_object_iter_next(object, iter);
if(!iter)
if (!iter)
fail("unable to increment iterator");
if (strcmp(json_object_iter_key(iter), "b") != 0)
fail("iterating doesn't yield keys in order");
@ -316,36 +429,36 @@ static void test_iterators()
fail("iterating doesn't yield values in order");
iter = json_object_iter_next(object, iter);
if(!iter)
if (!iter)
fail("unable to increment iterator");
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");
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);
@ -354,113 +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_del(object, "a"))
if (json_object_del(object, "a"))
fail("unable to del the only 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, "a", NULL))
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);
@ -468,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();
@ -494,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");
}
@ -503,53 +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 test_object_foreach_safe()
{
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);
}
json_object_foreach_safe(object, tmp, key, value) { json_object_del(object, key); }
if(json_object_size(object) != 0)
if (json_object_size(object) != 0)
fail("json_object_foreach_safe failed to iterate all key-value pairs");
json_decref(object);
}
static void run_tests()
{
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

@ -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,245 +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)))
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)
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))
if (!json_is_null(value))
fail("json_pack nullable string (NULL case) failed");
if(value->refcount != (size_t)-1)
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)
if (!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack nullable object (defined case) failed");
if(value->refcount != (size_t)1)
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))
if (!json_is_null(value))
fail("json_pack nullable object (NULL case) failed");
if(value->refcount != (size_t)-1)
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)
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");
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))
if (!json_is_null(value))
fail("json_pack incref'd nullable object (NULL case) failed");
if(value->refcount != (size_t)-1)
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);
@ -276,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

@ -5,88 +5,134 @@
* 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,113 +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))
if (!json_is_string(test))
fail("value type check failed");
}
if(value->refcount != 1)
fail("automatic decrement 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

@ -6,13 +6,12 @@
* 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,133 +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, s:b}", "foo", "bar", "baz", 42, "quux", 1);
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
if (!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
fail("json_unpack object with strict validation failed");
{
const char *possible_errors[] = {
"2 object item(s) left unpacked: baz, quux",
"2 object item(s) left unpacked: quux, baz"
};
check_errors(possible_errors, 2, "<validation>", 1, 10, 10);
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: baz", "<validation>", 1, 7, 7);
check_error(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz",
"<validation>", 1, 7, 7);
json_decref(j);
/* Error in nested array */
j = json_pack("[[ii]]", 1, 2);
if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1))
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: baz", "<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

@ -22,60 +22,63 @@
#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_errors(texts_, num_, source_, line_, column_, position_) \
do { \
int i_, found_ = 0; \
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)
#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(text_, source_, line_, column_, position_) \
check_errors(&text_, 1, source_, line_, column_, position_)
#define check_error(code_, text_, source_, line_, column_, position_) \
check_errors(code_, &text_, 1, source_, line_, column_, position_)
static void run_tests();

View file

@ -1,2 +1 @@
JSON_COMPACT=1
export JSON_COMPACT

View file

@ -1,3 +1,2 @@
JSON_COMPACT=1
HASHSEED=1
export JSON_COMPACT HASHSEED

View file

@ -1,2 +1 @@
JSON_ENSURE_ASCII=1
export JSON_ENSURE_ASCII

View file

@ -1,2 +1 @@
JSON_INDENT=4
export JSON_INDENT

View file

@ -1,3 +1,2 @@
JSON_INDENT=4
JSON_COMPACT=1
export JSON_INDENT JSON_COMPACT

View file

@ -1,4 +1,3 @@
JSON_INDENT=4
JSON_COMPACT=1
HASHSEED=1
export JSON_INDENT JSON_COMPACT HASHSEED

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