Compare commits
448 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dddf687d8 | ||
|
|
744fe5ed44 | ||
|
|
03620980cf | ||
|
|
248d62111c | ||
|
|
46dff2737d | ||
|
|
fa0b5ece9e | ||
|
|
a6138a07b6 | ||
|
|
2863dde053 | ||
|
|
efe6c7b3f2 | ||
|
|
3e81f78366 | ||
|
|
8104ce167a | ||
|
|
f44921e176 | ||
|
|
3aee856d7b | ||
|
|
37e0ee4d48 | ||
|
|
dc3b313e91 | ||
|
|
45228cada4 | ||
|
|
24d45272a7 | ||
|
|
9e5af7c3b7 | ||
|
|
6c78910011 | ||
|
|
89dad8959b | ||
|
|
9a1d9c88fc | ||
|
|
02dade46c0 | ||
|
|
bc5c6826ef | ||
|
|
217859f849 | ||
|
|
3951d39b40 | ||
|
|
bd91753e91 | ||
|
|
0b04762c94 | ||
|
|
009ffa3fc8 | ||
|
|
89f0dde7ff | ||
|
|
9e7847ed26 | ||
|
|
112ccbd820 | ||
|
|
271ffda903 | ||
|
|
3e5405c39e | ||
|
|
93e8cd7d68 | ||
|
|
0abcbce3bb | ||
|
|
4947f9a193 | ||
|
|
ad6c1e37ad | ||
|
|
f52c3da717 | ||
|
|
28666cead0 | ||
|
|
74028ff958 | ||
|
|
fbf720f2c5 | ||
|
|
1b8bebf0bf | ||
|
|
f7a70de84a | ||
|
|
17f77cf2c6 | ||
|
|
b23201bb1a | ||
|
|
df454e3cf0 | ||
|
|
b8bb078cc2 | ||
|
|
3c51112063 | ||
|
|
1672bb5a65 | ||
|
|
b900967f6f | ||
|
|
746c2c3a99 | ||
|
|
2af820fb99 | ||
|
|
bc5741fb1a | ||
|
|
575f951b3e | ||
|
|
0cac862bbc | ||
|
|
4467bf243f | ||
|
|
ddd1e1f223 | ||
|
|
d1e97737d6 | ||
|
|
98be7da3e2 | ||
|
|
08cb7b6d6f | ||
|
|
b02db47881 | ||
|
|
074bb3838f | ||
|
|
3ba3b23fdc | ||
|
|
e9fcab08fb | ||
|
|
bdaf7584db | ||
|
|
889280c976 | ||
|
|
f9e7aa5eeb | ||
|
|
9258671924 | ||
|
|
a2bbb44d96 | ||
|
|
16b516f976 | ||
|
|
86196250b8 | ||
|
|
ada5372cff | ||
|
|
f11c1b9466 | ||
|
|
811a30691e | ||
|
|
7d1af52ab4 | ||
|
|
63b9fd0552 | ||
|
|
b45745118d | ||
|
|
0ffecdbade | ||
|
|
ab1ba69027 | ||
|
|
a5610c8895 | ||
|
|
012c5f0eca | ||
|
|
a931aace16 | ||
|
|
14573dc920 | ||
|
|
71594af7d5 | ||
|
|
6a4b3f878d | ||
|
|
86fdf76f79 | ||
|
|
835290dfdf | ||
|
|
7daffabf07 | ||
|
|
9df267054f | ||
|
|
8f067962f6 | ||
|
|
603fdc9154 | ||
|
|
520340998f | ||
|
|
2d710d832f | ||
|
|
a8c834c882 | ||
|
|
7438cc8ba8 | ||
|
|
72fd2fec4c | ||
|
|
762b299e71 | ||
|
|
013c3892c3 | ||
|
|
64ce0ad373 | ||
|
|
087ed94c45 | ||
|
|
006638a6a2 | ||
|
|
130c2fff31 | ||
|
|
cfd817895a | ||
|
|
ac97e0bf79 | ||
|
|
1927eeb4b4 | ||
|
|
c8361441fe | ||
|
|
107cfe9499 | ||
|
|
811965b475 | ||
|
|
e08101704c | ||
|
|
c17be5870b | ||
|
|
9d71d006cc | ||
|
|
b698ca13de | ||
|
|
0f50bb10b6 | ||
|
|
e6b60da043 | ||
|
|
89f4c15e6d | ||
|
|
1c2e707b34 | ||
|
|
52015cf35c | ||
|
|
1d513d063a | ||
|
|
581d5b899c | ||
|
|
4f49c07781 | ||
|
|
245e532934 | ||
|
|
e44b2231b5 | ||
|
|
40bd71f064 | ||
|
|
067dc50efe | ||
|
|
4876bda857 | ||
|
|
e89538f685 | ||
|
|
f7331c7194 | ||
|
|
02464862ed | ||
|
|
db0213ae56 | ||
|
|
8f2298bad8 | ||
|
|
4c4f692bd6 | ||
|
|
875b78dc97 | ||
|
|
fef27e6d3e | ||
|
|
1dab656dee | ||
|
|
69678aaa35 | ||
|
|
d384acd706 | ||
|
|
5d42e1520a | ||
|
|
d8753db4ac | ||
|
|
95dd927857 | ||
|
|
76760011ff | ||
|
|
11813f4128 | ||
|
|
8b1bdcacb7 | ||
|
|
c242b46016 | ||
|
|
58c188e1d5 | ||
|
|
ffc18128f4 | ||
|
|
6a38d0d431 | ||
|
|
d0a8ad4c06 | ||
|
|
c244b1483e | ||
|
|
fc83f10c85 | ||
|
|
8d561cd94e | ||
|
|
970c6988a5 | ||
|
|
220dcb7be3 | ||
|
|
48e0488f07 | ||
|
|
890760b2fb | ||
|
|
15653c47dd | ||
|
|
5508ab403d | ||
|
|
d799ee11b4 | ||
|
|
abaae7630e | ||
|
|
5c1d87592a | ||
|
|
5885035f5f | ||
|
|
dee4a7c29e | ||
|
|
82a55ef205 | ||
|
|
19f33c0e71 | ||
|
|
3c9e5c9925 | ||
|
|
1c38ab17f5 | ||
|
|
d5edfcc6fd | ||
|
|
9b435df3d4 | ||
|
|
bc743ad2d9 | ||
|
|
19cc800ad3 | ||
|
|
b52e7a69aa | ||
|
|
1395e4303a | ||
|
|
d7a6269a17 | ||
|
|
7fbe7c3960 | ||
|
|
ee27b7e3dd | ||
|
|
391d9101a8 | ||
|
|
2137e0c895 | ||
|
|
5fc44e10aa | ||
|
|
b94d767f86 | ||
|
|
43f17d010a | ||
|
|
fa20e80860 | ||
|
|
485c7640a1 | ||
|
|
d4a7de7c11 | ||
|
|
a6229a2d3e | ||
|
|
abd151f5d7 | ||
|
|
4f1238af71 | ||
|
|
122a1e2af9 | ||
|
|
c8d017bd88 | ||
|
|
23b1b7ba9a | ||
|
|
88943b64e3 | ||
|
|
348401e7b8 | ||
|
|
df248712a5 | ||
|
|
de1b5db70e | ||
|
|
4debe8e567 | ||
|
|
28d362cf28 | ||
|
|
541cdf5960 | ||
|
|
fc6e314498 | ||
|
|
17b5fdd94b | ||
|
|
5b88cc5ded | ||
|
|
db285b3764 | ||
|
|
a425f8c650 | ||
|
|
96f8916e50 | ||
|
|
dd2bf2971e | ||
|
|
34a64cfe53 | ||
|
|
571617ebfc | ||
|
|
3bd8a5729d | ||
|
|
33e4988180 | ||
|
|
663fbfb7a4 | ||
|
|
88aa6a9e30 | ||
|
|
92e2588d6e | ||
|
|
960ead07f2 | ||
|
|
78da1de021 | ||
|
|
946531bd7b | ||
|
|
10009d61a7 | ||
|
|
49fc6b1194 | ||
|
|
6e8b2e161a | ||
|
|
3922f84a2f | ||
|
|
56a50e147d | ||
|
|
7a0b9af662 | ||
|
|
4fbe44605b | ||
|
|
ea7a77236c | ||
|
|
1dc87ed5a1 | ||
|
|
50a811ca07 | ||
|
|
4bbb53a25d | ||
|
|
569295fe30 | ||
|
|
3f5acaa3fb | ||
|
|
42016a35c8 | ||
|
|
17ec22f514 | ||
|
|
e83ded066a | ||
|
|
492feb26ce | ||
|
|
8f80c2d838 | ||
|
|
b9c588de3d | ||
|
|
a721d36f41 | ||
|
|
eee85cd53c | ||
|
|
7c80778827 | ||
|
|
4c2cc0e36c | ||
|
|
a630e226ba | ||
|
|
0c95a22888 | ||
|
|
30fdf6067e | ||
|
|
cffc5df600 | ||
|
|
3fe8f74e7f | ||
|
|
913937c98d | ||
|
|
b21cd65d30 | ||
|
|
c89638d73c | ||
|
|
98610bfcec | ||
|
|
9c8b3c833f | ||
|
|
dc69aa797b | ||
|
|
4e8c4bfbd2 | ||
|
|
316492e4d0 | ||
|
|
3347679d8f | ||
|
|
b951baec0d | ||
|
|
05f7d30e5a | ||
|
|
34d8b92dce | ||
|
|
67a7bc7376 | ||
|
|
30a4c88843 | ||
|
|
08be94e8e6 | ||
|
|
d544852ff6 | ||
|
|
8dc3233f3b | ||
|
|
c7d479c740 | ||
|
|
a76dc45512 | ||
|
|
9bb5a266dd | ||
|
|
6ebd0bc7a6 | ||
|
|
f8d8d524cf | ||
|
|
1bfc33362e | ||
|
|
5744468c99 | ||
|
|
4d5aead31c | ||
|
|
78a80b8899 | ||
|
|
b961e8101e | ||
|
|
7876125a22 | ||
|
|
e2dcf94598 | ||
|
|
6c1ce27095 | ||
|
|
46e27ae6d5 | ||
|
|
0be5c959da | ||
|
|
face43929d | ||
|
|
dcaa90d21e | ||
|
|
9c259c07aa | ||
|
|
e4d6a9f6f4 | ||
|
|
641002da37 | ||
|
|
9f9c9fe410 | ||
|
|
e39112b29c | ||
|
|
bf2584703a | ||
|
|
db8ca3645f | ||
|
|
0490907fb3 | ||
|
|
c2e8fdde9d | ||
|
|
24c67966aa | ||
|
|
0747d3943f | ||
|
|
5f9ef108d0 | ||
|
|
21599b95f8 | ||
|
|
3196ad48ed | ||
|
|
49ad5328c7 | ||
|
|
71676acd0b | ||
|
|
2489ea90b1 | ||
|
|
11d45b0090 | ||
|
|
8490e377c0 | ||
|
|
ef666519f7 | ||
|
|
6fe231757e | ||
|
|
84b5bfe173 | ||
|
|
d9ee5a7f1b | ||
|
|
19a19d1952 | ||
|
|
68e201add8 | ||
|
|
62531bd012 | ||
|
|
f639fbd2c3 | ||
|
|
a38704df58 | ||
|
|
8d439710cd | ||
|
|
120a90a155 | ||
|
|
c3e9725f02 | ||
|
|
02a3829363 | ||
|
|
018f7fea31 | ||
|
|
e6bd0aba9d | ||
|
|
9e7f11a847 | ||
|
|
042d4b2a6b | ||
|
|
ddd10b5e0b | ||
|
|
2fd66fab1a | ||
|
|
5cc594c9e8 | ||
|
|
ffb7ef4b75 | ||
|
|
b49280be72 | ||
|
|
7121134abd | ||
|
|
e8c812b500 | ||
|
|
6950cd203b | ||
|
|
d3bd463897 | ||
|
|
54d86fb4a4 | ||
|
|
42d398243d | ||
|
|
f675514815 | ||
|
|
5793142861 | ||
|
|
b227f96c45 | ||
|
|
0687442f5d | ||
|
|
8390c90a91 | ||
|
|
3000831365 | ||
|
|
6b1cba94e3 | ||
|
|
9591d3a155 | ||
|
|
83d04ddd68 | ||
|
|
70232676f7 | ||
|
|
257a716073 | ||
|
|
0ccd2c7715 | ||
|
|
42e307b3c5 | ||
|
|
61cba2ae50 | ||
|
|
867079c820 | ||
|
|
d20751d53d | ||
|
|
6281d1c549 | ||
|
|
8fd475bf96 | ||
|
|
bbd3b0ca4f | ||
|
|
7c4f323abc | ||
|
|
1edff47af3 | ||
|
|
dc2d54d9c6 | ||
|
|
263fd0c9d3 | ||
|
|
6db70f0301 | ||
|
|
70f663db29 | ||
|
|
59bc1f42e4 | ||
|
|
b1b4f307d5 | ||
|
|
a3e6369105 | ||
|
|
3d0d61fdaf | ||
|
|
650707fccc | ||
|
|
33b0855b27 | ||
|
|
7214a222c7 | ||
|
|
81ce127048 | ||
|
|
344d2b00ea | ||
|
|
52a8072727 | ||
|
|
741e6915e2 | ||
|
|
e40d52c550 | ||
|
|
3d5bea5714 | ||
|
|
3e03b07831 | ||
|
|
e00cd4f941 | ||
|
|
a1882fee02 | ||
|
|
4a5626695c | ||
|
|
afa1d37e20 | ||
|
|
b9967fdbcf | ||
|
|
ccf6e48a52 | ||
|
|
93a3c7f663 | ||
|
|
5dbac70a67 | ||
|
|
40c2e532b9 | ||
|
|
02beb90db3 | ||
|
|
92bf4f6fa8 | ||
|
|
8ad98c9ad3 | ||
|
|
7a4ec36f31 | ||
|
|
3fab956599 | ||
|
|
a2de27a828 | ||
|
|
99855b2d63 | ||
|
|
08c5ec8f01 | ||
|
|
e9cb9dbf60 | ||
|
|
8284b7d3da | ||
|
|
511db446d7 | ||
|
|
b98be1f18d | ||
|
|
872f847655 | ||
|
|
f86bb0377f | ||
|
|
c82cea9d32 | ||
|
|
67c002f7c9 | ||
|
|
e0a7f81b39 | ||
|
|
7b35a18ac0 | ||
|
|
54d59c743c | ||
|
|
6279610ce4 | ||
|
|
ac0ca9223b | ||
|
|
4853a3454c | ||
|
|
cf1074e70e | ||
|
|
5804e9de9b | ||
|
|
3279aacdee | ||
|
|
2158670177 | ||
|
|
4cff593dd4 | ||
|
|
597423ea80 | ||
|
|
c3fc1d7382 | ||
|
|
c922354076 | ||
|
|
4118315afa | ||
|
|
ee13c667f1 | ||
|
|
23d563434a | ||
|
|
6142dbd8d0 | ||
|
|
0dac319bc4 | ||
|
|
0b871a113c | ||
|
|
8176527f56 | ||
|
|
ec7bb71d75 | ||
|
|
b6a1d8cfd4 | ||
|
|
52924288b9 | ||
|
|
7892ecce1c | ||
|
|
a501a39626 | ||
|
|
37bc3bbf4b | ||
|
|
2d46ea069b | ||
|
|
b217cd6689 | ||
|
|
a0c262d08b | ||
|
|
6ce273e2e6 | ||
|
|
f62b1f5d69 | ||
|
|
2b87fdcb43 | ||
|
|
c0139681cd | ||
|
|
9d6f9511f5 | ||
|
|
a79f64e155 | ||
|
|
7ca783c3bc | ||
|
|
ff0c05b8f1 | ||
|
|
4601bf71e5 | ||
|
|
c7f86abf6d | ||
|
|
f309e30320 | ||
|
|
233574e8e0 | ||
|
|
4a6939ef87 | ||
|
|
42bc7a3c50 | ||
|
|
abdb8d99d7 | ||
|
|
8b2bfd5586 | ||
|
|
1581f26a7f | ||
|
|
e8fd3e3085 | ||
|
|
873eddaf19 | ||
|
|
bd2c0c730d | ||
|
|
17a51a4bf0 | ||
|
|
09c39adc55 | ||
|
|
cbb80baf03 | ||
|
|
040bd7b0fa | ||
|
|
952e1d4ba9 | ||
|
|
d286e7b753 | ||
|
|
9af64480e1 | ||
|
|
56039ed596 | ||
|
|
1eb274c555 | ||
|
|
f736e705b2 | ||
|
|
98a99fb2bd | ||
|
|
60fd7ab781 | ||
|
|
a894980258 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -19,10 +19,15 @@ install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
compile
|
||||
test-driver
|
||||
*.lo
|
||||
*.la
|
||||
stamp-h1
|
||||
*.pyc
|
||||
*.pc
|
||||
/src/jansson_config.h
|
||||
/jansson_private_config.h.in
|
||||
/jansson_private_config.h
|
||||
/build
|
||||
*.exe
|
||||
|
||||
22
.travis.yml
Normal file
22
.travis.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
30
Android.mk
Normal file
30
Android.mk
Normal file
@@ -0,0 +1,30 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_ARM_MODE := arm
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
src/dump.c \
|
||||
src/error.c \
|
||||
src/hashtable.c \
|
||||
src/hashtable_seed.c \
|
||||
src/load.c \
|
||||
src/memory.c \
|
||||
src/pack_unpack.c \
|
||||
src/strbuffer.c \
|
||||
src/strconv.c \
|
||||
src/utf.c \
|
||||
src/value.c
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(LOCAL_PATH) \
|
||||
$(LOCAL_PATH)/android \
|
||||
$(LOCAL_PATH)/src
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SHARED_LIBRARIES := libc
|
||||
LOCAL_CFLAGS += -O3 -DHAVE_STDINT_H=1
|
||||
|
||||
LOCAL_MODULE:= libjansson
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
410
CHANGES
410
CHANGES
@@ -1,3 +1,413 @@
|
||||
Version 2.11
|
||||
============
|
||||
|
||||
Released 2018-02-09
|
||||
|
||||
* New features:
|
||||
|
||||
- Add `json_pack()` format specifiers s*, o* and O* for values that
|
||||
can be omitted if null (#339).
|
||||
|
||||
- Add `json_error_code()` to retrieve numeric error codes (#365, #380,
|
||||
#381).
|
||||
|
||||
- Enable thread safety for `json_dump()` on all systems. Enable thread
|
||||
safe `json_decref()` and `json_incref()` for modern compilers (#389).
|
||||
|
||||
- Add `json_sprintf()` and `json_vsprintf()` (#393).
|
||||
|
||||
* Bug Fixes:
|
||||
|
||||
- Fix incorrect report of success from `json_dump_file()` when an error
|
||||
is returned by `fclose()` (#359).
|
||||
|
||||
- Make json_equal() const-correct (#344).
|
||||
|
||||
- Fix incomplete stealing of references by `json_pack()` (#374).
|
||||
|
||||
* Build:
|
||||
|
||||
- Work around gcc's -Wimplicit-fallthrough.
|
||||
|
||||
- Fix CMake detection of `sys/types.h` header (#375).
|
||||
|
||||
- Fix `jansson.pc` generated by CMake to be more consistent with the one
|
||||
generated using GNU Autotools (#368).
|
||||
|
||||
* Other:
|
||||
|
||||
- Miscellaneous documentation fixes (#356, #378, #395).
|
||||
|
||||
- Remove unnecessary reference actions from parsers (#377).
|
||||
|
||||
Version 2.10
|
||||
============
|
||||
|
||||
Released 2017-03-02
|
||||
|
||||
* New features:
|
||||
|
||||
- Add JSON_EMBED encoding flag allowing arrays and objects to be encoded
|
||||
into existing streams (#329).
|
||||
|
||||
- Add `json_dumpb()` function for dumping to a pre-allocated buffer (#328).
|
||||
|
||||
- Add `json_dumpfd()` and `json_loadfd()` functions for dumping to streaming
|
||||
file descriptors (#328).
|
||||
|
||||
- Add support for parsing buffers larger than 2GB (#309).
|
||||
|
||||
* Build:
|
||||
|
||||
- Fix CMake build when LONG_LONG_INT is defined as "" (#321)
|
||||
|
||||
* Other:
|
||||
|
||||
- Internal code cleanup (#311, #314)
|
||||
|
||||
Version 2.9
|
||||
===========
|
||||
|
||||
Released 2016-09-18
|
||||
|
||||
* New features:
|
||||
|
||||
- Add ``json_auto_t`` to automatically decref a value that goes out
|
||||
of scope. Available only on GCC and Clang. (#301)
|
||||
|
||||
* Build:
|
||||
|
||||
- Fix CMake build (at least on Linux) by removing conflicting
|
||||
jansson_config.h from the distribution (#306)
|
||||
|
||||
- Change CMake install target generation to be optional (#305)
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Small documentation fixes.
|
||||
|
||||
|
||||
Version 2.8
|
||||
===========
|
||||
|
||||
Released 2016-08-30
|
||||
|
||||
* New features:
|
||||
|
||||
- Always preserve insertion order of object items.
|
||||
`json_object_iter()` and friends, `json_object_foreach()` and
|
||||
`json_dumps()` and friends now always work in the insertion order of
|
||||
object items (#293).
|
||||
|
||||
- Add `json_object_foreach_safe()` macro that allows
|
||||
`json_object_del()` calls during iteration (#230).
|
||||
|
||||
- Add `json_get_alloc_funcs()` to allow reading the allocation
|
||||
functions set by `json_set_alloc_funcs()` (#262, #264).
|
||||
|
||||
- Add `json_pack()` format specifiers s?, o? and O? for values that
|
||||
can be null (#261, #270).
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix a crash when parsing inputs consisting of very deeply nested
|
||||
arrays or objects (#282, #284).
|
||||
|
||||
- Never convert numbers to integers in the parser when
|
||||
JSON_DECODE_INT_AS_REAL is set. This fixes error messages for
|
||||
overflowing numbers when JSON_DECODE_INT_AS_REAL is set (#212).
|
||||
|
||||
- Fix a use-after-free in `json_pack()` error handling.
|
||||
|
||||
- Fix subnormal number parsing on mingw32.
|
||||
|
||||
- Handle out-of-memory situations gracefully in the hashtable
|
||||
implementation (#298).
|
||||
|
||||
* Build:
|
||||
|
||||
- Fix build with CMake on all versions of Visual Studio up to 2015
|
||||
(#262, #289).
|
||||
|
||||
- Fix pkgconfig libdir when using CMake (#268).
|
||||
|
||||
- Fix CMake config for static CRT builds on Windows (#206).
|
||||
|
||||
- Fix warnings on LLVM 6.0 targeting iOS arm64 (#208).
|
||||
|
||||
- Add coverlls.io support via Travis for a nice test coverage badge
|
||||
(#211).
|
||||
|
||||
- Don't expect ``jansson_config.h`` to be in the compiler's include
|
||||
path (#209).
|
||||
|
||||
- Add a build-time option to set initial hashtable size (#213).
|
||||
|
||||
- Use snprintf and strncpy in place of sprintf and strcpy to silence
|
||||
linker warnings on OpenBSD (#233).
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Fix various typos in documentation, and a broken link (#258).
|
||||
|
||||
- Add an example program in ``examples/`` (#214, #217).
|
||||
|
||||
- Fix building of documentation man pages (#207).
|
||||
|
||||
- Document the fact that copying objects doesn't preserve the
|
||||
insertion order of keys (#237).
|
||||
|
||||
* Tests:
|
||||
|
||||
- Don't use the nonstandard __FUNCTION__ macro in tests.
|
||||
|
||||
- Use expr instead of $((...)) in shell scripts for Solaris 10
|
||||
compatibility.
|
||||
|
||||
- Disable Visual Studio warning C4756 when triggered deliberately in
|
||||
tests (#216).
|
||||
|
||||
- Other minor fixes (#221, #248).
|
||||
|
||||
* Other changes:
|
||||
|
||||
- List all unrecognized object keys when strict unpacking fails
|
||||
(#263).
|
||||
|
||||
- Alter the order of the members of the hashtable_pair struct for
|
||||
easier debugging.
|
||||
|
||||
- Minor performance improvement to `json_dump()` and friends (#234).
|
||||
|
||||
- Minor style fixes (#255, #257).
|
||||
|
||||
|
||||
Version 2.7
|
||||
===========
|
||||
|
||||
Released 2014-10-02
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_pack()` and friends: Add format specifiers ``s%`` and ``+%``
|
||||
for a size_t string length (#141).
|
||||
|
||||
- `json_unpack()` and friends: Add format specifier ``s%`` for
|
||||
unpacking the string length along with the string itself (#141).
|
||||
|
||||
- Add length-aware string constructors `json_stringn()` and
|
||||
`json_stringn_nocheck()`, length-aware string mutators
|
||||
`json_string_setn()` and `json_string_setn_nocheck()`, and a
|
||||
function for getting string's length `json_string_length()` (#141,
|
||||
#143).
|
||||
|
||||
- Support ``\u0000`` escapes in the decoder. The support can be
|
||||
enabled by using the ``JSON_ALLOW_NUL`` decoding flag (#141).
|
||||
|
||||
- Add `json_boolean_value()` as an alias for `json_is_true()`
|
||||
(#146).
|
||||
|
||||
- Add JSON_REAL_PRECISION encoding flag/macro for controlling real
|
||||
number precision (#178).
|
||||
|
||||
- Define the maximum indentation as JSON_MAX_INDENT (#191).
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Some malformed ``\uNNNN`` escapes could crash the decoder with an
|
||||
assertion failure.
|
||||
|
||||
- Avoid integer overflows with very long strings in UTF-8 decoder and
|
||||
hashtable.
|
||||
|
||||
- Check for *NULL* key in `json_object_get()` and
|
||||
`json_object_del()` (#151).
|
||||
|
||||
- Enhance hashtable seeding on Windows (#162).
|
||||
|
||||
- `json_unpack()`: Allow mixing JSON_STRICT with optional keys
|
||||
(#162, #163).
|
||||
|
||||
- Fix int/int32 mismatch (#142).
|
||||
|
||||
- Parse subnormal numbers correctly (#202).
|
||||
|
||||
* Build:
|
||||
|
||||
- Remove VS2010 build files. CMake should be used on Windows instead
|
||||
(#165).
|
||||
|
||||
- Fix CMake build flags for MinGW (#193).
|
||||
|
||||
- Add CMake config files for find_package. Rename config.h to
|
||||
jansson_private_config.h (#157, #159).
|
||||
|
||||
- Make Valgrind checks work with CMake (#160).
|
||||
|
||||
- Fix feature checks to use correct __ATOMIC flags.
|
||||
|
||||
- Fix CMake checks for uint16_t and uint8_t support (#177).
|
||||
|
||||
- Make Jansson build on SmartOS/Solaris (#171).
|
||||
|
||||
- Work around a GCC bug on Solaris (#175).
|
||||
|
||||
- Fix autoreconf on Debian (#182).
|
||||
|
||||
- Don't use GNU make specific export for global AM_CFLAGS (#203,
|
||||
#204).
|
||||
|
||||
- Fix building on Android using the supplied Android.mk (#166,
|
||||
#174).
|
||||
|
||||
- Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS (#200).
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Document JANSSON_BUILD_SHARED_LIBS CMake option (#187).
|
||||
|
||||
* Tests:
|
||||
|
||||
- Close file handles correctly (#198).
|
||||
|
||||
* Other changes:
|
||||
|
||||
- ``\uNNNN`` escapes are now encoded in upper case for better
|
||||
readability.
|
||||
|
||||
- Enable usage of AddressSanitizer (#180).
|
||||
|
||||
|
||||
Version 2.6
|
||||
===========
|
||||
|
||||
Released 2014-02-11
|
||||
|
||||
* Security:
|
||||
|
||||
- CVE-2013-6401: The hash function used by the hashtable
|
||||
implementation has been changed, and is automatically seeded with
|
||||
random data when the first JSON object is created. This prevents
|
||||
an attacker from causing large JSON objects with specially crafted
|
||||
keys perform poorly.
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_object_seed()`: Set the seed value of the hash function.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Include CMake specific files in the release tarball.
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Fix tutorial source to send a User-Agent header, which is now
|
||||
required by the GitHub API.
|
||||
|
||||
- Set all memory to zero in secure_free() example.
|
||||
|
||||
|
||||
Version 2.5
|
||||
===========
|
||||
|
||||
Released 2013-09-19
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and
|
||||
``+#``.
|
||||
|
||||
- Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers
|
||||
as real in the decoder (#123).
|
||||
|
||||
- Add `json_array_foreach()`, paralleling `json_object_foreach()`
|
||||
(#118).
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- `json_dumps()` and friends: Don't crash if json is *NULL* and
|
||||
``JSON_ENCODE_ANY`` is set.
|
||||
|
||||
- Fix a theoretical integer overflow in `jsonp_strdup()`.
|
||||
|
||||
- Fix `l_isxdigit()` macro (#97).
|
||||
|
||||
- Fix an off-by-one error in `json_array_remove()`.
|
||||
|
||||
* Build:
|
||||
|
||||
- Support CMake in addition to GNU Autotools (#106, #107, #112,
|
||||
#115, #120, #127).
|
||||
|
||||
- Support building for Android (#109).
|
||||
|
||||
- Don't use ``-Werror`` by default.
|
||||
|
||||
- Support building and testing with VPATH (#93).
|
||||
|
||||
- Fix compilation when ``NDEBUG`` is defined (#128)
|
||||
|
||||
* Tests:
|
||||
|
||||
- Fix a refleak in ``test/bin/json_process.c``.
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Clarify the return value of `json_load_callback_t`.
|
||||
|
||||
- Document how to circumvent problems with separate heaps on Windows.
|
||||
|
||||
- Fix memory leaks and warnings in ``github_commits.c``.
|
||||
|
||||
- Use `json_decref()` properly in tutorial.
|
||||
|
||||
* Other:
|
||||
|
||||
- Make it possible to forward declare ``struct json_t``.
|
||||
|
||||
|
||||
Version 2.4
|
||||
===========
|
||||
|
||||
Released 2012-09-23
|
||||
|
||||
* New features:
|
||||
|
||||
- Add `json_boolean()` macro that returns the JSON true or false
|
||||
value based on its argument (#86).
|
||||
|
||||
- Add `json_load_callback()` that calls a callback function
|
||||
repeatedly to read the JSON input (#57).
|
||||
|
||||
- Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of
|
||||
``/`` with ``\/``.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Check for and reject NaN and Inf values for reals. Encoding these
|
||||
values resulted in invalid JSON.
|
||||
|
||||
- Fix `json_real_set()` to return -1 on error.
|
||||
|
||||
* Build:
|
||||
|
||||
- Jansson now builds on Windows with Visual Studio 2010, and
|
||||
includes solution and project files in ``win32/vs2010/``
|
||||
directory.
|
||||
|
||||
- Fix build warnings (#77, #78).
|
||||
|
||||
- Add ``-no-undefined`` to LDFLAGS (#90).
|
||||
|
||||
* Tests:
|
||||
|
||||
- Fix the symbol exports test on Linux/PPC64 (#88).
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Fix typos (#73, #84).
|
||||
|
||||
|
||||
Version 2.3.1
|
||||
=============
|
||||
|
||||
|
||||
686
CMakeLists.txt
Normal file
686
CMakeLists.txt
Normal file
@@ -0,0 +1,686 @@
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
if (MSVC)
|
||||
# This option must match the settings used in your program, in particular if you
|
||||
# are linking statically
|
||||
option(JANSSON_STATIC_CRT "Link the static CRT libraries" OFF )
|
||||
endif ()
|
||||
|
||||
option(JANSSON_EXAMPLES "Compile example applications" ON)
|
||||
|
||||
if (UNIX)
|
||||
option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
set(CMAKE_DEBUG_POSTFIX "_d")
|
||||
endif (WIN32)
|
||||
|
||||
# This is how I thought it should go
|
||||
# set (JANSSON_VERSION "2.3.1")
|
||||
# set (JANSSON_SOVERSION 2)
|
||||
|
||||
set(JANSSON_DISPLAY_VERSION "2.11")
|
||||
|
||||
# This is what is required to match the same numbers as automake's
|
||||
set(JANSSON_VERSION "4.11.0")
|
||||
set(JANSSON_SOVERSION 4)
|
||||
|
||||
# for CheckFunctionKeywords
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include (CheckCSourceCompiles)
|
||||
include (CheckFunctionExists)
|
||||
include (CheckFunctionKeywords)
|
||||
include (CheckIncludeFiles)
|
||||
include (CheckTypeSize)
|
||||
|
||||
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()
|
||||
endif()
|
||||
|
||||
check_include_files (endian.h HAVE_ENDIAN_H)
|
||||
check_include_files (fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files (sched.h HAVE_SCHED_H)
|
||||
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/types.h HAVE_SYS_TYPES_H)
|
||||
|
||||
check_function_exists (close HAVE_CLOSE)
|
||||
check_function_exists (getpid HAVE_GETPID)
|
||||
check_function_exists (gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_function_exists (open HAVE_OPEN)
|
||||
check_function_exists (read HAVE_READ)
|
||||
check_function_exists (sched_yield HAVE_SCHED_YIELD)
|
||||
|
||||
# Check for the int-type includes
|
||||
check_include_files (stdint.h HAVE_STDINT_H)
|
||||
|
||||
# Check our 64 bit integer sizes
|
||||
check_type_size (__int64 __INT64)
|
||||
check_type_size (int64_t INT64_T)
|
||||
check_type_size ("long long" LONG_LONG_INT)
|
||||
|
||||
# Check our 32 bit integer sizes
|
||||
check_type_size (int32_t INT32_T)
|
||||
check_type_size (__int32 __INT32)
|
||||
check_type_size ("long" LONG_INT)
|
||||
check_type_size ("int" INT)
|
||||
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))
|
||||
set (JSON_INT32 long)
|
||||
elseif (HAVE_INT AND (${INT} EQUAL 4))
|
||||
set (JSON_INT32 int)
|
||||
else ()
|
||||
message (FATAL_ERROR "Could not detect a valid 32-bit integer type")
|
||||
endif ()
|
||||
|
||||
check_type_size ("unsigned long" UNSIGNED_LONG_INT)
|
||||
check_type_size ("unsigned int" UNSIGNED_INT)
|
||||
check_type_size ("unsigned short" UNSIGNED_SHORT)
|
||||
|
||||
check_type_size (uint32_t UINT32_T)
|
||||
check_type_size (__uint32 __UINT32)
|
||||
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))
|
||||
set (JSON_UINT32 "unsigned long")
|
||||
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")
|
||||
endif ()
|
||||
|
||||
check_type_size (uint16_t UINT16_T)
|
||||
check_type_size (__uint16 __UINT16)
|
||||
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))
|
||||
set (JSON_UINT16 "unsigned int")
|
||||
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")
|
||||
endif ()
|
||||
|
||||
check_type_size (uint8_t UINT8_T)
|
||||
check_type_size (__uint8 __UINT8)
|
||||
if (HAVE_UINT8_T)
|
||||
set (JSON_UINT8 uint8_t)
|
||||
elseif (HAVE___UINT8)
|
||||
set (JSON_UINT8 __uint8)
|
||||
else ()
|
||||
set (JSON_UINT8 "unsigned char")
|
||||
endif ()
|
||||
|
||||
# Check for ssize_t and SSIZE_T existance.
|
||||
check_type_size(ssize_t SSIZE_T)
|
||||
check_type_size(SSIZE_T UPPERCASE_SSIZE_T)
|
||||
if(NOT HAVE_SSIZE_T)
|
||||
if(HAVE_UPPERCASE_SSIZE_T)
|
||||
set(JSON_SSIZE SSIZE_T)
|
||||
else()
|
||||
set(JSON_SSIZE int)
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "")
|
||||
|
||||
# Check for all the variants of strtoll
|
||||
check_function_exists (strtoll HAVE_STRTOLL)
|
||||
check_function_exists (strtoq HAVE_STRTOQ)
|
||||
check_function_exists (_strtoi64 HAVE__STRTOI64)
|
||||
|
||||
# Figure out what variant we should use
|
||||
if (HAVE_STRTOLL)
|
||||
set (JSON_STRTOINT strtoll)
|
||||
elseif (HAVE_STRTOQ)
|
||||
set (JSON_STRTOINT strtoq)
|
||||
elseif (HAVE__STRTOI64)
|
||||
set (JSON_STRTOINT _strtoi64)
|
||||
else ()
|
||||
# fallback to strtol (32 bit)
|
||||
# this will set all the required variables
|
||||
set (JSON_STRTOINT strtol)
|
||||
set (JSON_INT_T long)
|
||||
set (JSON_INTEGER_FORMAT "\"ld\"")
|
||||
endif ()
|
||||
|
||||
# if we haven't defined JSON_INT_T, then we have a 64 bit conversion function.
|
||||
# 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_INT_T "long long")
|
||||
elseif (HAVE_INT64_T)
|
||||
set (JSON_INT_T int64_t)
|
||||
elseif (HAVE___INT64)
|
||||
set (JSON_INT_T __int64)
|
||||
else ()
|
||||
message (FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent")
|
||||
endif ()
|
||||
|
||||
# Apparently, Borland BCC and MSVC wants I64d,
|
||||
# Borland BCC could also accept LD
|
||||
# and gcc wants ldd,
|
||||
# I am not sure what cygwin will want, so I will assume I64d
|
||||
|
||||
if (WIN32) # matches both msvc and cygwin
|
||||
set (JSON_INTEGER_FORMAT "\"I64d\"")
|
||||
else ()
|
||||
set (JSON_INTEGER_FORMAT "\"lld\"")
|
||||
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.
|
||||
# Note that the original JSON_INLINE was always set to just 'inline', so this goes further.
|
||||
check_function_keywords("inline")
|
||||
check_function_keywords("__inline")
|
||||
check_function_keywords("__inline__")
|
||||
|
||||
if (HAVE_INLINE)
|
||||
set(JSON_INLINE inline)
|
||||
elseif (HAVE___INLINE)
|
||||
set(JSON_INLINE __inline)
|
||||
elseif (HAVE___INLINE__)
|
||||
set(JSON_INLINE __inline__)
|
||||
else()
|
||||
# no inline on this platform
|
||||
set (JSON_INLINE)
|
||||
endif()
|
||||
|
||||
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1); return 0; } " HAVE_SYNC_BUILTINS)
|
||||
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE); return 0; }" HAVE_ATOMIC_BUILTINS)
|
||||
|
||||
set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.")
|
||||
|
||||
# configure the public config file
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h)
|
||||
|
||||
# Copy the jansson.h file to the public include folder
|
||||
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)
|
||||
|
||||
# Add the lib sources.
|
||||
file(GLOB JANSSON_SRC src/*.c)
|
||||
|
||||
set(JANSSON_HDR_PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hashtable.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson_private.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/strbuffer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utf.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
|
||||
|
||||
set(JANSSON_HDR_PUBLIC
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h)
|
||||
|
||||
source_group("Library Sources" FILES ${JANSSON_SRC})
|
||||
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}
|
||||
src/jansson.def)
|
||||
|
||||
set_target_properties(jansson PROPERTIES
|
||||
VERSION ${JANSSON_VERSION}
|
||||
SOVERSION ${JANSSON_SOVERSION})
|
||||
else()
|
||||
add_library(jansson
|
||||
${JANSSON_SRC}
|
||||
${JANSSON_HDR_PRIVATE}
|
||||
${JANSSON_HDR_PUBLIC})
|
||||
endif()
|
||||
|
||||
if (JANSSON_EXAMPLES)
|
||||
add_executable(simple_parse "${PROJECT_SOURCE_DIR}/examples/simple_parse.c")
|
||||
target_link_libraries(simple_parse jansson)
|
||||
endif()
|
||||
|
||||
# For building Documentation (uses Sphinx)
|
||||
option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
|
||||
if (JANSSON_BUILD_DOCS)
|
||||
find_package(Sphinx)
|
||||
|
||||
if (NOT SPHINX_FOUND)
|
||||
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
|
||||
proper documentation (you have v${Sphinx_VERSION_STRING}).
|
||||
You will get output but it will have errors.")
|
||||
endif()
|
||||
|
||||
# configured documentation tools and intermediate build results
|
||||
set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build")
|
||||
|
||||
# Sphinx cache with pickled ReST documents
|
||||
set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
|
||||
|
||||
# CMake could be used to build the conf.py file too,
|
||||
# eg it could automatically write the version of the program or change the theme.
|
||||
# if(NOT DEFINED SPHINX_THEME)
|
||||
# set(SPHINX_THEME default)
|
||||
# endif()
|
||||
#
|
||||
# if(NOT DEFINED SPHINX_THEME_DIR)
|
||||
# set(SPHINX_THEME_DIR)
|
||||
# endif()
|
||||
#
|
||||
# configure_file(
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in"
|
||||
# "${BINARY_BUILD_DIR}/conf.py"
|
||||
# @ONLY)
|
||||
|
||||
# TODO: Add support for all sphinx builders: http://sphinx-doc.org/builders.html
|
||||
|
||||
# Add documentation targets.
|
||||
set(DOC_TARGETS html)
|
||||
|
||||
option(JANSSON_BUILD_MAN "Create a target for building man pages." ON)
|
||||
|
||||
if (JANSSON_BUILD_MAN)
|
||||
if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
|
||||
message(WARNING "Sphinx version 1.0 > is required to build man pages. You have v${Sphinx_VERSION_STRING}.")
|
||||
else()
|
||||
list(APPEND DOC_TARGETS man)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(JANSSON_BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF)
|
||||
|
||||
if (JANSSON_BUILD_LATEX)
|
||||
find_package(LATEX)
|
||||
|
||||
if (NOT LATEX_COMPILER)
|
||||
message("Couldn't find Latex, can't build latex docs using Sphinx")
|
||||
else()
|
||||
message("Latex found! If you have problems building, see Sphinx documentation for required Latex packages.")
|
||||
list(APPEND DOC_TARGETS latex)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The doc target will build all documentation targets.
|
||||
add_custom_target(doc)
|
||||
|
||||
foreach (DOC_TARGET ${DOC_TARGETS})
|
||||
add_custom_target(${DOC_TARGET}
|
||||
${SPHINX_EXECUTABLE}
|
||||
# -q # Enable for quiet mode
|
||||
-b ${DOC_TARGET}
|
||||
-d "${SPHINX_CACHE_DIR}"
|
||||
# -c "${BINARY_BUILD_DIR}" # enable if using cmake-generated conf.py
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/doc"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/doc/${DOC_TARGET}"
|
||||
COMMENT "Building ${DOC_TARGET} documentation with Sphinx")
|
||||
|
||||
add_dependencies(doc ${DOC_TARGET})
|
||||
endforeach()
|
||||
|
||||
message("Building documentation enabled for: ${DOC_TARGETS}")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
|
||||
option(JANSSON_WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF)
|
||||
|
||||
if (NOT JANSSON_WITHOUT_TESTS)
|
||||
option(JANSSON_TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
|
||||
|
||||
ENABLE_TESTING()
|
||||
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
# TODO: Add FindValgrind.cmake instead of having a hardcoded path.
|
||||
|
||||
add_definitions(-DVALGRIND)
|
||||
|
||||
# enable valgrind
|
||||
set(CMAKE_MEMORYCHECK_COMMAND valgrind)
|
||||
set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS
|
||||
"--error-exitcode=1 --leak-check=full --show-reachable=yes --track-origins=yes -q")
|
||||
|
||||
set(MEMCHECK_COMMAND
|
||||
"${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}")
|
||||
separate_arguments(MEMCHECK_COMMAND)
|
||||
endif ()
|
||||
|
||||
#
|
||||
# Test suites.
|
||||
#
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
add_definitions(-Wall -Wextra -Wdeclaration-after-statement)
|
||||
endif ()
|
||||
|
||||
set(api_tests
|
||||
test_array
|
||||
test_copy
|
||||
test_dump
|
||||
test_dump_callback
|
||||
test_equal
|
||||
test_load
|
||||
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
|
||||
# such as secure_malloc and secure_free is doing, so exclude it for now.
|
||||
if (NOT MSVC)
|
||||
list(APPEND api_tests test_memory_funcs)
|
||||
endif()
|
||||
|
||||
# Helper macro for building and linking a test program.
|
||||
macro(build_testprog name dir)
|
||||
add_executable(${name} ${dir}/${name}.c)
|
||||
add_dependencies(${name} jansson)
|
||||
target_link_libraries(${name} jansson)
|
||||
endmacro(build_testprog)
|
||||
|
||||
# Create executables and tests/valgrind tests for API tests.
|
||||
foreach (test ${api_tests})
|
||||
build_testprog(${test} ${PROJECT_SOURCE_DIR}/test/suites/api)
|
||||
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
add_test(memcheck__${test}
|
||||
${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
|
||||
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
|
||||
else()
|
||||
add_test(${test}
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
|
||||
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Test harness for the suites tests.
|
||||
build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin)
|
||||
|
||||
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}/*)
|
||||
|
||||
foreach (TESTDIR ${TESTDIRS})
|
||||
if (IS_DIRECTORY ${TESTDIR})
|
||||
get_filename_component(TNAME ${TESTDIR} NAME)
|
||||
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
add_test(memcheck__${SUITE}__${TNAME}
|
||||
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR})
|
||||
else()
|
||||
add_test(${SUITE}__${TNAME}
|
||||
${SUITE_TEST_CMD} ${TESTDIR})
|
||||
endif()
|
||||
|
||||
if ((${SUITE} STREQUAL "valid" OR ${SUITE} STREQUAL "invalid") AND NOT EXISTS ${TESTDIR}/nostrip)
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
add_test(memcheck__${SUITE}__${TNAME}__strip
|
||||
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} --strip ${TESTDIR})
|
||||
else()
|
||||
add_test(${SUITE}__${TNAME}__strip
|
||||
${SUITE_TEST_CMD} --strip ${TESTDIR})
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
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 ()
|
||||
endif ()
|
||||
|
||||
# Enable using "make check" just like the autotools project.
|
||||
# By default cmake creates a target "make test"
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
|
||||
DEPENDS json_process ${api_tests})
|
||||
endif ()
|
||||
|
||||
#
|
||||
# Installation preparation.
|
||||
#
|
||||
|
||||
# Allow the user to override installation directories.
|
||||
set(JANSSON_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
|
||||
set(JANSSON_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables")
|
||||
set(JANSSON_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files")
|
||||
|
||||
if(WIN32 AND NOT CYGWIN)
|
||||
set(DEF_INSTALL_CMAKE_DIR cmake)
|
||||
else()
|
||||
set(DEF_INSTALL_CMAKE_DIR lib/cmake/jansson)
|
||||
endif()
|
||||
|
||||
set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
|
||||
|
||||
# Create pkg-conf file.
|
||||
# (We use the same files as ./configure does, so we
|
||||
# have to defined the same variables used there).
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix "\${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.
|
||||
foreach(p LIB BIN INCLUDE CMAKE)
|
||||
set(var JANSSON_INSTALL_${p}_DIR)
|
||||
if(NOT IS_ABSOLUTE "${${var}}")
|
||||
set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Export targets (This is used for other CMake projects to easily find the libraries and include files).
|
||||
export(TARGETS jansson
|
||||
FILE "${PROJECT_BINARY_DIR}/JanssonTargets.cmake")
|
||||
export(PACKAGE jansson)
|
||||
|
||||
# Generate the config file for the build-tree.
|
||||
set(JANSSON__INCLUDE_DIRS
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${PROJECT_BINARY_DIR}/include")
|
||||
set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories")
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
|
||||
${PROJECT_BINARY_DIR}/JanssonConfig.cmake
|
||||
@ONLY)
|
||||
|
||||
# Generate the config file for the installation tree.
|
||||
file(RELATIVE_PATH
|
||||
REL_INCLUDE_DIR
|
||||
"${JANSSON_INSTALL_CMAKE_DIR}"
|
||||
"${JANSSON_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir.
|
||||
|
||||
# Note the EVENT_CMAKE_DIR is defined in JanssonConfig.cmake.in,
|
||||
# we escape it here so it's evaluated when it is included instead
|
||||
# so that the include dirs are given relative to where the
|
||||
# config file is located.
|
||||
set(JANSSON__INCLUDE_DIRS
|
||||
"\${JANSSON_CMAKE_DIR}/${REL_INCLUDE_DIR}")
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
|
||||
${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
|
||||
@ONLY)
|
||||
|
||||
# Generate version info for both build-tree and install-tree.
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfigVersion.cmake.in
|
||||
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
|
||||
@ONLY)
|
||||
|
||||
# Define the public headers.
|
||||
set_target_properties(jansson PROPERTIES PUBLIC_HEADER "${JANSSON_HDR_PUBLIC}")
|
||||
#TODO: fix this.
|
||||
|
||||
#
|
||||
# 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 the pkg-config.
|
||||
install (FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
|
||||
DESTINATION ${JANSSON_INSTALL_LIB_DIR}/pkgconfig COMPONENT dev)
|
||||
|
||||
# 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 exports for the install-tree.
|
||||
install(EXPORT JanssonTargets
|
||||
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
|
||||
endif()
|
||||
|
||||
# For use when simply using add_library from a parent project to build jansson.
|
||||
set(JANSSON_LIBRARIES jansson CACHE STRING "Jansson libraries")
|
||||
49
CleanSpec.mk
Normal file
49
CleanSpec.mk
Normal file
@@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2007 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# If you don't need to do a full clean build but would like to touch
|
||||
# a file or delete some intermediate files, add a clean step to the end
|
||||
# of the list. These steps will only be run once, if they haven't been
|
||||
# run before.
|
||||
#
|
||||
# E.g.:
|
||||
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
|
||||
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
|
||||
#
|
||||
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
|
||||
# files that are missing or have been moved.
|
||||
#
|
||||
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
|
||||
# Use $(OUT_DIR) to refer to the "out" directory.
|
||||
#
|
||||
# If you need to re-do something that's already mentioned, just copy
|
||||
# the command and add it to the bottom of the list. E.g., if a change
|
||||
# that you made last week required touching a file and a change you
|
||||
# made today requires touching the same file, just copy the old
|
||||
# touch step and add it to the end of the list.
|
||||
#
|
||||
# ************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||
# ************************************************
|
||||
|
||||
# For example:
|
||||
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
|
||||
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
|
||||
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
|
||||
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
|
||||
|
||||
# ************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||
# ************************************************
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
Copyright (c) 2009-2016 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android examples
|
||||
SUBDIRS = doc src test
|
||||
|
||||
# "make distcheck" builds the dvi target, so use it to check that the
|
||||
|
||||
19
README.rst
19
README.rst
@@ -1,12 +1,21 @@
|
||||
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
|
||||
|
||||
.. image:: https://coveralls.io/repos/akheron/jansson/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/akheron/jansson?branch=master
|
||||
|
||||
Jansson_ is a C library for encoding, decoding and manipulating JSON
|
||||
data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Comprehensive documentation
|
||||
- `Comprehensive documentation`_
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
@@ -33,8 +42,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 fist. The easiest way is to use
|
||||
autoreconf::
|
||||
./configure script has to be generated first. The easiest way is to
|
||||
use autoreconf::
|
||||
|
||||
$ autoreconf -i
|
||||
|
||||
@@ -42,8 +51,7 @@ autoreconf::
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Prebuilt HTML documentation is available at
|
||||
http://www.digip.org/jansson/doc/.
|
||||
Documentation is available at http://jansson.readthedocs.io/en/latest/.
|
||||
|
||||
The documentation source is in the ``doc/`` subdirectory. To generate
|
||||
HTML documentation, invoke::
|
||||
@@ -55,5 +63,6 @@ Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
|
||||
|
||||
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
.. _`Comprehensive documentation`: http://jansson.readthedocs.io/en/latest/
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifdef __cplusplus
|
||||
#define JSON_INLINE inline
|
||||
#else
|
||||
#define JSON_INLINE
|
||||
#define JSON_INLINE inline
|
||||
#endif
|
||||
|
||||
/* If your compiler supports the `long long` type and the strtoll()
|
||||
@@ -34,6 +34,10 @@
|
||||
|
||||
/* If locale.h and localeconv() are available, define to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV 1
|
||||
#define JSON_HAVE_LOCALECONV 0
|
||||
|
||||
/* Maximum recursion depth for parsing JSON input.
|
||||
This limits the depth of e.g. array-within-array constructions. */
|
||||
#define JSON_PARSER_MAX_DEPTH 2048
|
||||
|
||||
#endif
|
||||
14
appveyor.yml
Normal file
14
appveyor.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
environment:
|
||||
matrix:
|
||||
- VS: Visual Studio 9 2008
|
||||
- VS: Visual Studio 10 2010
|
||||
- VS: Visual Studio 11 2012
|
||||
- VS: Visual Studio 12 2013
|
||||
- VS: Visual Studio 14 2015
|
||||
|
||||
build_script:
|
||||
- md build
|
||||
- cd build
|
||||
- cmake -G "%VS%" ..
|
||||
- cmake --build . --config Release
|
||||
- ctest --output-on-failure
|
||||
15
cmake/CheckFunctionKeywords.cmake
Normal file
15
cmake/CheckFunctionKeywords.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
macro(check_function_keywords _wordlist)
|
||||
set(${_result} "")
|
||||
foreach(flag ${_wordlist})
|
||||
string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}")
|
||||
string(TOUPPER "${flagname}" flagname)
|
||||
set(have_flag "HAVE_${flagname}")
|
||||
check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag})
|
||||
if(${have_flag} AND NOT ${_result})
|
||||
set(${_result} "${flag}")
|
||||
# break()
|
||||
endif(${have_flag} AND NOT ${_result})
|
||||
endforeach(flag)
|
||||
endmacro(check_function_keywords)
|
||||
163
cmake/CodeCoverage.cmake
Normal file
163
cmake/CodeCoverage.cmake
Normal file
@@ -0,0 +1,163 @@
|
||||
#
|
||||
# Boost Software License - Version 1.0 - August 17th, 2003
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person or organization
|
||||
# obtaining a copy of the software and accompanying documentation covered by
|
||||
# this license (the "Software") to use, reproduce, display, distribute,
|
||||
# execute, and transmit the Software, and to prepare derivative works of the
|
||||
# Software, and to permit third-parties to whom the Software is furnished to
|
||||
# do so, all subject to the following:
|
||||
#
|
||||
# The copyright notices in the Software and this entire statement, including
|
||||
# the above license grant, this restriction and the following disclaimer,
|
||||
# must be included in all copies of the Software, in whole or in part, and
|
||||
# all derivative works of the Software, unless such copies or derivative
|
||||
# works are solely in the form of machine-executable object code generated by
|
||||
# a source language processor.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# USAGE:
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt:
|
||||
# INCLUDE(CodeCoverage)
|
||||
#
|
||||
# 3. Set compiler flags to turn off optimization and enable coverage:
|
||||
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
#
|
||||
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
|
||||
# which runs your test executable and produces a lcov code coverage report:
|
||||
# Example:
|
||||
# SETUP_TARGET_FOR_COVERAGE(
|
||||
# my_coverage_target # Name for custom target.
|
||||
# test_driver # Name of the test driver executable that runs the tests.
|
||||
# # NOTE! This should always have a ZERO as exit code
|
||||
# # otherwise the coverage generation will not complete.
|
||||
# coverage # Name of output directory.
|
||||
# )
|
||||
#
|
||||
# 4. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
#
|
||||
|
||||
# Check prereqs
|
||||
FIND_PROGRAM( GCOV_PATH gcov )
|
||||
FIND_PROGRAM( LCOV_PATH lcov )
|
||||
FIND_PROGRAM( GENHTML_PATH genhtml )
|
||||
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
|
||||
|
||||
IF(NOT GCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
|
||||
ENDIF() # NOT GCOV_PATH
|
||||
|
||||
IF(NOT (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC))
|
||||
# Clang version 3.0.0 and greater now supports gcov as well.
|
||||
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
|
||||
|
||||
IF(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
|
||||
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...")
|
||||
ENDIF()
|
||||
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
|
||||
|
||||
IF ( NOT CMAKE_BUILD_TYPE STREQUAL "Debug" )
|
||||
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
|
||||
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _outputname lcov output is generated as _outputname.info
|
||||
# HTML report is generated in _outputname/index.html
|
||||
# Param _testrunner The name of the target which runs the tests.
|
||||
# MUST return ZERO always, even on errors.
|
||||
# If not, no coverage report will be created!
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner)
|
||||
|
||||
IF(NOT LCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
|
||||
ENDIF() # NOT LCOV_PATH
|
||||
|
||||
IF(NOT GENHTML_PATH)
|
||||
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
ENDIF() # NOT GENHTML_PATH
|
||||
|
||||
# Setup target
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Cleanup lcov
|
||||
${LCOV_PATH} --directory . --zerocounters
|
||||
|
||||
# Run tests
|
||||
COMMAND ${_testrunner} ${ARGV3}
|
||||
|
||||
# Capturing lcov counters and generating report
|
||||
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
|
||||
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
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests
|
||||
# Param _outputname cobertura output is generated as _outputname.xml
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT PYTHON_EXECUTABLE)
|
||||
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
|
||||
ENDIF() # NOT PYTHON_EXECUTABLE
|
||||
|
||||
IF(NOT GCOVR_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
ENDIF() # NOT GCOVR_PATH
|
||||
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Run tests
|
||||
${_testrunner} ${ARGV3}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
|
||||
|
||||
111
cmake/Coveralls.cmake
Normal file
111
cmake/Coveralls.cmake
Normal file
@@ -0,0 +1,111 @@
|
||||
#
|
||||
# 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()
|
||||
|
||||
|
||||
|
||||
24
cmake/CoverallsClear.cmake
Normal file
24
cmake/CoverallsClear.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# 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)
|
||||
|
||||
380
cmake/CoverallsGenerateGcov.cmake
Normal file
380
cmake/CoverallsGenerateGcov.cmake
Normal file
@@ -0,0 +1,380 @@
|
||||
#
|
||||
# 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("###########################################################################")
|
||||
|
||||
301
cmake/FindSphinx.cmake
Normal file
301
cmake/FindSphinx.cmake
Normal file
@@ -0,0 +1,301 @@
|
||||
#
|
||||
# PART B. DOWNLOADING AGREEMENT - LICENSE FROM SBIA WITH RIGHT TO SUBLICENSE ("SOFTWARE LICENSE").
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
#
|
||||
# 1. As used in this Software License, "you" means the individual downloading and/or
|
||||
# using, reproducing, modifying, displaying and/or distributing the Software and
|
||||
# the institution or entity which employs or is otherwise affiliated with such
|
||||
# individual in connection therewith. The Section of Biomedical Image Analysis,
|
||||
# Department of Radiology at the Universiy of Pennsylvania ("SBIA") hereby grants
|
||||
# you, with right to sublicense, with respect to SBIA's rights in the software,
|
||||
# and data, if any, which is the subject of this Software License (collectively,
|
||||
# the "Software"), a royalty-free, non-exclusive license to use, reproduce, make
|
||||
# derivative works of, display and distribute the Software, provided that:
|
||||
# (a) you accept and adhere to all of the terms and conditions of this Software
|
||||
# License; (b) in connection with any copy of or sublicense of all or any portion
|
||||
# of the Software, all of the terms and conditions in this Software License shall
|
||||
# appear in and shall apply to such copy and such sublicense, including without
|
||||
# limitation all source and executable forms and on any user documentation,
|
||||
# prefaced with the following words: "All or portions of this licensed product
|
||||
# (such portions are the "Software") have been obtained under license from the
|
||||
# Section of Biomedical Image Analysis, Department of Radiology at the University
|
||||
# of Pennsylvania and are subject to the following terms and conditions:"
|
||||
# (c) you preserve and maintain all applicable attributions, copyright notices
|
||||
# and licenses included in or applicable to the Software; (d) modified versions
|
||||
# of the Software must be clearly identified and marked as such, and must not
|
||||
# be misrepresented as being the original Software; and (e) you consider making,
|
||||
# but are under no obligation to make, the source code of any of your modifications
|
||||
# to the Software freely available to others on an open source basis.
|
||||
#
|
||||
# 2. The license granted in this Software License includes without limitation the
|
||||
# right to (i) incorporate the Software into proprietary programs (subject to
|
||||
# any restrictions applicable to such programs), (ii) add your own copyright
|
||||
# statement to your modifications of the Software, and (iii) provide additional
|
||||
# or different license terms and conditions in your sublicenses of modifications
|
||||
# of the Software; provided that in each case your use, reproduction or
|
||||
# distribution of such modifications otherwise complies with the conditions
|
||||
# stated in this Software License.
|
||||
#
|
||||
# 3. This Software License does not grant any rights with respect to third party
|
||||
# software, except those rights that SBIA has been authorized by a third
|
||||
# party to grant to you, and accordingly you are solely responsible for
|
||||
# (i) obtaining any permissions from third parties that you need to use,
|
||||
# reproduce, make derivative works of, display and distribute the Software,
|
||||
# and (ii) informing your sublicensees, including without limitation your
|
||||
# end-users, of their obligations to secure any such required permissions.
|
||||
#
|
||||
# 4. The Software has been designed for research purposes only and has not been
|
||||
# reviewed or approved by the Food and Drug Administration or by any other
|
||||
# agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL APPLICATIONS ARE NEITHER
|
||||
# RECOMMENDED NOR ADVISED. Any commercialization of the Software is at the
|
||||
# sole risk of the party or parties engaged in such commercialization.
|
||||
# You further agree to use, reproduce, make derivative works of, display
|
||||
# and distribute the Software in compliance with all applicable governmental
|
||||
# laws, regulations and orders, including without limitation those relating
|
||||
# to export and import control.
|
||||
#
|
||||
# 5. The Software is provided "AS IS" and neither SBIA nor any contributor to
|
||||
# the software (each a "Contributor") shall have any obligation to provide
|
||||
# maintenance, support, updates, enhancements or modifications thereto.
|
||||
# SBIA AND ALL CONTRIBUTORS SPECIFICALLY DISCLAIM ALL EXPRESS AND IMPLIED
|
||||
# WARRANTIES OF ANY KIND INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
# IN NO EVENT SHALL SBIA OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR
|
||||
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY RELATED
|
||||
# TO THE SOFTWARE, EVEN IF SBIA OR ANY CONTRIBUTOR HAS BEEN ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM EXTENT NOT PROHIBITED BY LAW OR
|
||||
# REGULATION, YOU FURTHER ASSUME ALL LIABILITY FOR YOUR USE, REPRODUCTION,
|
||||
# MAKING OF DERIVATIVE WORKS, DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE
|
||||
# AND AGREE TO INDEMNIFY AND HOLD HARMLESS SBIA AND ALL CONTRIBUTORS FROM
|
||||
# AND AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS ARISING
|
||||
# THEREFROM.
|
||||
#
|
||||
# 6. None of the names, logos or trademarks of SBIA or any of SBIA's affiliates
|
||||
# or any of the Contributors, or any funding agency, may be used to endorse
|
||||
# or promote products produced in whole or in part by operation of the Software
|
||||
# or derived from or based on the Software without specific prior written
|
||||
# permission from the applicable party.
|
||||
#
|
||||
# 7. Any use, reproduction or distribution of the Software which is not in accordance
|
||||
# with this Software License shall automatically revoke all rights granted to you
|
||||
# under this Software License and render Paragraphs 1 and 2 of this Software
|
||||
# License null and void.
|
||||
#
|
||||
# 8. This Software License does not grant any rights in or to any intellectual
|
||||
# property owned by SBIA or any Contributor except those rights expressly
|
||||
# granted hereunder.
|
||||
#
|
||||
#
|
||||
# PART C. MISCELLANEOUS
|
||||
# ---------------------
|
||||
#
|
||||
# This Agreement shall be governed by and construed in accordance with the laws
|
||||
# of The Commonwealth of Pennsylvania without regard to principles of conflicts
|
||||
# of law. This Agreement shall supercede and replace any license terms that you
|
||||
# may have agreed to previously with respect to Software from SBIA.
|
||||
#
|
||||
##############################################################################
|
||||
# @file FindSphinx.cmake
|
||||
# @brief Find Sphinx documentation build tools.
|
||||
#
|
||||
# @par Input variables:
|
||||
# <table border="0">
|
||||
# <tr>
|
||||
# @tp @b Sphinx_DIR @endtp
|
||||
# <td>Installation directory of Sphinx tools. Can also be set as environment variable.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b SPHINX_DIR @endtp
|
||||
# <td>Alternative environment variable for @c Sphinx_DIR.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_FIND_COMPONENTS @endtp
|
||||
# <td>Sphinx build tools to look for, i.e., 'apidoc' and/or 'build'.</td>
|
||||
# </tr>
|
||||
# </table>
|
||||
#
|
||||
# @par Output variables:
|
||||
# <table border="0">
|
||||
# <tr>
|
||||
# @tp @b Sphinx_FOUND @endtp
|
||||
# <td>Whether all or only the requested Sphinx build tools were found.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b SPHINX_FOUND @endtp
|
||||
# <td>Alias for @c Sphinx_FOUND.<td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b SPHINX_EXECUTABLE @endtp
|
||||
# <td>Non-cached alias for @c Sphinx-build_EXECUTABLE.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_PYTHON_EXECUTABLE @endtp
|
||||
# <td>Python executable used to run sphinx-build. This is either the
|
||||
# by default found Python interpreter or a specific version as
|
||||
# specified by the shebang (#!) of the sphinx-build script.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_PYTHON_OPTIONS @endtp
|
||||
# <td>A list of Python options extracted from the shebang (#!) of the
|
||||
# sphinx-build script. The -E option is added by this module
|
||||
# if the Python executable is not the system default to avoid
|
||||
# problems with a differing setting of the @c PYTHONHOME.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx-build_EXECUTABLE @endtp
|
||||
# <td>Absolute path of the found sphinx-build tool.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx-apidoc_EXECUTABLE @endtp
|
||||
# <td>Absolute path of the found sphinx-apidoc tool.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_VERSION_STRING @endtp
|
||||
# <td>Sphinx version found e.g. 1.1.2.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_VERSION_MAJOR @endtp
|
||||
# <td>Sphinx major version found e.g. 1.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_VERSION_MINOR @endtp
|
||||
# <td>Sphinx minor version found e.g. 1.</td>
|
||||
# </tr>
|
||||
# <tr>
|
||||
# @tp @b Sphinx_VERSION_PATCH @endtp
|
||||
# <td>Sphinx patch version found e.g. 2.</td>
|
||||
# </tr>
|
||||
# </table>
|
||||
#
|
||||
# @ingroup CMakeFindModules
|
||||
##############################################################################
|
||||
|
||||
set (_Sphinx_REQUIRED_VARS)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# initialize search
|
||||
if (NOT Sphinx_DIR)
|
||||
if (NOT $ENV{Sphinx_DIR} STREQUAL "")
|
||||
set (Sphinx_DIR "$ENV{Sphinx_DIR}" CACHE PATH "Installation prefix of Sphinx (docutils)." FORCE)
|
||||
else ()
|
||||
set (Sphinx_DIR "$ENV{SPHINX_DIR}" CACHE PATH "Installation prefix of Sphinx (docutils)." FORCE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# default components to look for
|
||||
if (NOT Sphinx_FIND_COMPONENTS)
|
||||
set (Sphinx_FIND_COMPONENTS "build")
|
||||
elseif (NOT Sphinx_FIND_COMPONENTS MATCHES "^(build|apidoc)$")
|
||||
message (FATAL_ERROR "Invalid Sphinx component in: ${Sphinx_FIND_COMPONENTS}")
|
||||
endif ()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# find components, i.e., build tools
|
||||
foreach (_Sphinx_TOOL IN LISTS Sphinx_FIND_COMPONENTS)
|
||||
if (Sphinx_DIR)
|
||||
find_program (
|
||||
Sphinx-${_Sphinx_TOOL}_EXECUTABLE
|
||||
NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py
|
||||
HINTS "${Sphinx_DIR}"
|
||||
PATH_SUFFIXES bin
|
||||
DOC "The sphinx-${_Sphinx_TOOL} Python script."
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
else ()
|
||||
find_program (
|
||||
Sphinx-${_Sphinx_TOOL}_EXECUTABLE
|
||||
NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py
|
||||
DOC "The sphinx-${_Sphinx_TOOL} Python script."
|
||||
)
|
||||
endif ()
|
||||
mark_as_advanced (Sphinx-${_Sphinx_TOOL}_EXECUTABLE)
|
||||
list (APPEND _Sphinx_REQUIRED_VARS Sphinx-${_Sphinx_TOOL}_EXECUTABLE)
|
||||
endforeach ()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# determine Python executable used by Sphinx
|
||||
if (Sphinx-build_EXECUTABLE)
|
||||
# extract python executable from shebang of sphinx-build
|
||||
find_package (PythonInterp QUIET)
|
||||
set (Sphinx_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}")
|
||||
set (Sphinx_PYTHON_OPTIONS)
|
||||
file (STRINGS "${Sphinx-build_EXECUTABLE}" FIRST_LINE LIMIT_COUNT 1)
|
||||
if (FIRST_LINE MATCHES "^#!(.*/python.*)") # does not match "#!/usr/bin/env python" !
|
||||
string (REGEX REPLACE "^ +| +$" "" Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}")
|
||||
if (Sphinx_PYTHON_EXECUTABLE MATCHES "([^ ]+) (.*)")
|
||||
set (Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}")
|
||||
string (REGEX REPLACE " +" ";" Sphinx_PYTHON_OPTIONS "${CMAKE_MATCH_2}")
|
||||
endif ()
|
||||
endif ()
|
||||
# this is done to avoid problems with multiple Python versions being installed
|
||||
# remember: CMake command if(STR EQUAL STR) is bad and may cause many troubles !
|
||||
string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" _Sphinx_PYTHON_EXECUTABLE_RE "${PYTHON_EXECUTABLE}")
|
||||
list (FIND Sphinx_PYTHON_OPTIONS -E IDX)
|
||||
if (IDX EQUAL -1 AND NOT Sphinx_PYTHON_EXECUTABLE MATCHES "^${_Sphinx_PYTHON_EXECUTABLE_RE}$")
|
||||
list (INSERT Sphinx_PYTHON_OPTIONS 0 -E)
|
||||
endif ()
|
||||
unset (_Sphinx_PYTHON_EXECUTABLE_RE)
|
||||
endif ()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# determine Sphinx version
|
||||
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
|
||||
OUTPUT_VARIABLE _Sphinx_VERSION
|
||||
ERROR_VARIABLE _Sphinx_VERSION
|
||||
)
|
||||
elseif (UNIX)
|
||||
execute_process (
|
||||
COMMAND "${Sphinx-build_EXECUTABLE}" -h
|
||||
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]+)")
|
||||
set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}")
|
||||
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})
|
||||
|
||||
# v1.2.0 -> v1.2
|
||||
if (Sphinx_VERSION_PATCH EQUAL 0)
|
||||
string (REGEX REPLACE "\\.0$" "" Sphinx_VERSION_STRING "${Sphinx_VERSION_STRING}")
|
||||
endif ()
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# compatibility with FindPythonInterp.cmake and FindPerl.cmake
|
||||
set (SPHINX_EXECUTABLE "${Sphinx-build_EXECUTABLE}")
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# handle the QUIETLY and REQUIRED arguments and set SPHINX_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include (FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS (
|
||||
Sphinx
|
||||
REQUIRED_VARS
|
||||
${_Sphinx_REQUIRED_VARS}
|
||||
# VERSION_VAR # This isn't available until CMake 2.8.8 so don't use it.
|
||||
Sphinx_VERSION_STRING
|
||||
)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# set Sphinx_DIR
|
||||
if (NOT Sphinx_DIR AND Sphinx-build_EXECUTABLE)
|
||||
get_filename_component (Sphinx_DIR "${Sphinx-build_EXECUTABLE}" PATH)
|
||||
string (REGEX REPLACE "/bin/?" "" Sphinx_DIR "${Sphinx_DIR}")
|
||||
set (Sphinx_DIR "${Sphinx_DIR}" CACHE PATH "Installation directory of Sphinx tools." FORCE)
|
||||
endif ()
|
||||
|
||||
unset (_Sphinx_VERSION)
|
||||
unset (_Sphinx_REQUIRED_VARS)
|
||||
17
cmake/JanssonConfig.cmake.in
Normal file
17
cmake/JanssonConfig.cmake.in
Normal file
@@ -0,0 +1,17 @@
|
||||
# - 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)
|
||||
|
||||
11
cmake/JanssonConfigVersion.cmake.in
Normal file
11
cmake/JanssonConfigVersion.cmake.in
Normal file
@@ -0,0 +1,11 @@
|
||||
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()
|
||||
68
cmake/jansson_config.h.cmake
Normal file
68
cmake/jansson_config.h.cmake
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
*
|
||||
* This file specifies a part of the site-specific configuration for
|
||||
* Jansson, namely those things that affect the public API in
|
||||
* jansson.h.
|
||||
*
|
||||
* The CMake system will generate the jansson_config.h file and
|
||||
* copy it to the build and install directories.
|
||||
*/
|
||||
|
||||
#ifndef JANSSON_CONFIG_H
|
||||
#define JANSSON_CONFIG_H
|
||||
|
||||
/* Define this so that we can disable scattered automake configuration in source files */
|
||||
#ifndef JANSSON_USING_CMAKE
|
||||
#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) */
|
||||
|
||||
/* Bring in the cmake-detected defines */
|
||||
#cmakedefine HAVE_STDINT_H 1
|
||||
#cmakedefine HAVE_INTTYPES_H 1
|
||||
#cmakedefine HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Include our standard type header for the integer typedef */
|
||||
|
||||
#if defined(HAVE_STDINT_H)
|
||||
# include <stdint.h>
|
||||
#elif defined(HAVE_INTTYPES_H)
|
||||
# include <inttypes.h>
|
||||
#elif defined(HAVE_SYS_TYPES_H)
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* If your compiler supports the inline keyword in C, JSON_INLINE is
|
||||
defined to `inline', otherwise empty. In C++, the inline is always
|
||||
supported. */
|
||||
#ifdef __cplusplus
|
||||
#define JSON_INLINE inline
|
||||
#else
|
||||
#define JSON_INLINE @JSON_INLINE@
|
||||
#endif
|
||||
|
||||
|
||||
#define json_int_t @JSON_INT_T@
|
||||
#define json_strtoint @JSON_STRTOINT@
|
||||
#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@
|
||||
|
||||
|
||||
/* 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
|
||||
53
cmake/jansson_private_config.h.cmake
Normal file
53
cmake/jansson_private_config.h.cmake
Normal file
@@ -0,0 +1,53 @@
|
||||
#cmakedefine HAVE_ENDIAN_H 1
|
||||
#cmakedefine HAVE_FCNTL_H 1
|
||||
#cmakedefine HAVE_SCHED_H 1
|
||||
#cmakedefine HAVE_UNISTD_H 1
|
||||
#cmakedefine HAVE_SYS_PARAM_H 1
|
||||
#cmakedefine HAVE_SYS_STAT_H 1
|
||||
#cmakedefine HAVE_SYS_TIME_H 1
|
||||
#cmakedefine HAVE_SYS_TYPES_H 1
|
||||
#cmakedefine HAVE_STDINT_H 1
|
||||
|
||||
#cmakedefine HAVE_CLOSE 1
|
||||
#cmakedefine HAVE_GETPID 1
|
||||
#cmakedefine HAVE_GETTIMEOFDAY 1
|
||||
#cmakedefine HAVE_OPEN 1
|
||||
#cmakedefine HAVE_READ 1
|
||||
#cmakedefine HAVE_SCHED_YIELD 1
|
||||
|
||||
#cmakedefine HAVE_SYNC_BUILTINS 1
|
||||
#cmakedefine HAVE_ATOMIC_BUILTINS 1
|
||||
|
||||
#cmakedefine HAVE_LOCALE_H 1
|
||||
#cmakedefine HAVE_SETLOCALE 1
|
||||
|
||||
#cmakedefine HAVE_INT32_T 1
|
||||
#ifndef HAVE_INT32_T
|
||||
# define int32_t @JSON_INT32@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT32_T 1
|
||||
#ifndef HAVE_UINT32_T
|
||||
# define uint32_t @JSON_UINT32@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT16_T 1
|
||||
#ifndef HAVE_UINT16_T
|
||||
# define uint16_t @JSON_UINT16@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT8_T 1
|
||||
#ifndef HAVE_UINT8_T
|
||||
# define uint8_t @JSON_UINT8@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_SSIZE_T 1
|
||||
|
||||
#ifndef HAVE_SSIZE_T
|
||||
# define ssize_t @JSON_SSIZE@
|
||||
#endif
|
||||
|
||||
#cmakedefine USE_URANDOM 1
|
||||
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
|
||||
|
||||
#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@
|
||||
77
configure.ac
77
configure.ac
@@ -1,10 +1,11 @@
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.3.1], [petri@digip.org])
|
||||
AC_INIT([jansson], [2.11], [petri@digip.org])
|
||||
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/value.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_HEADERS([jansson_private_config.h])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
@@ -14,10 +15,13 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
|
||||
# Checks for libraries.
|
||||
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([locale.h])
|
||||
AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/stat.h sys/time.h sys/types.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT8_T
|
||||
AC_TYPE_LONG_LONG_INT
|
||||
|
||||
AC_C_INLINE
|
||||
@@ -29,7 +33,39 @@ esac
|
||||
AC_SUBST([json_inline])
|
||||
|
||||
# Checks for library functions.
|
||||
AC_CHECK_FUNCS([strtoll localeconv])
|
||||
AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read 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); __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); __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
|
||||
yesyes) json_have_long_long=1;;
|
||||
@@ -43,6 +79,39 @@ case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
|
||||
esac
|
||||
AC_SUBST([json_have_localeconv])
|
||||
|
||||
# Features
|
||||
AC_ARG_ENABLE([urandom],
|
||||
[AS_HELP_STRING([--disable-urandom],
|
||||
[Don't use /dev/urandom to seed the hash function])],
|
||||
[use_urandom=$enableval], [use_urandom=yes])
|
||||
|
||||
if test "x$use_urandom" = xyes; then
|
||||
AC_DEFINE([USE_URANDOM], [1],
|
||||
[Define to 1 if /dev/urandom should be used for seeding the hash function])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([windows-cryptoapi],
|
||||
[AS_HELP_STRING([--disable-windows-cryptoapi],
|
||||
[Don't use CryptGenRandom to seed the hash function])],
|
||||
[use_windows_cryptoapi=$enableval], [use_windows_cryptoapi=yes])
|
||||
|
||||
if test "x$use_windows_cryptoapi" = xyes; then
|
||||
AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1],
|
||||
[Define to 1 if CryptGenRandom should be used for seeding the hash function])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([initial-hashtable-order],
|
||||
[AS_HELP_STRING([--enable-initial-hashtable-order=VAL],
|
||||
[Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.])],
|
||||
[initial_hashtable_order=$enableval], [initial_hashtable_order=3])
|
||||
AC_DEFINE_UNQUOTED([INITIAL_HASHTABLE_ORDER], [$initial_hashtable_order],
|
||||
[Number of buckets new object hashtables contain is 2 raised to this power. E.g. 3 -> 2^3 = 8.])
|
||||
|
||||
if test x$GCC = xyes; then
|
||||
AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement"
|
||||
fi
|
||||
AC_SUBST([AM_CFLAGS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
jansson.pc
|
||||
Makefile
|
||||
|
||||
706
doc/apiref.rst
706
doc/apiref.rst
File diff suppressed because it is too large
Load Diff
@@ -41,14 +41,14 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Jansson'
|
||||
copyright = u'2009-2012, Petri Lehtinen'
|
||||
copyright = u'2009-2016, 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.3.1'
|
||||
version = '2.11'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
||||
@@ -19,15 +19,11 @@ Strings
|
||||
=======
|
||||
|
||||
JSON strings are mapped to C-style null-terminated character arrays,
|
||||
and UTF-8 encoding is used internally. Strings may not contain
|
||||
embedded null characters, not even escaped ones.
|
||||
and UTF-8 encoding is used internally.
|
||||
|
||||
For example, trying to decode the following JSON text leads to a parse
|
||||
error::
|
||||
|
||||
["this string contains the null character: \u0000"]
|
||||
|
||||
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
|
||||
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.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
@@ -38,6 +34,8 @@ strings.
|
||||
Numbers
|
||||
=======
|
||||
|
||||
.. _real-vs-integer:
|
||||
|
||||
Real vs. Integer
|
||||
----------------
|
||||
|
||||
@@ -51,7 +49,8 @@ A JSON number is considered to be a real number if its lexical
|
||||
representation includes one of ``e``, ``E``, or ``.``; regardless if
|
||||
its actual numeric value is a true integer (e.g., all of ``1E6``,
|
||||
``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but
|
||||
will be treated as real values).
|
||||
will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL``
|
||||
decoder flag set all numbers are interpreted as real.
|
||||
|
||||
All other JSON numbers are considered integers.
|
||||
|
||||
@@ -109,3 +108,13 @@ types, ``long double``, etc. Obviously, shorter types like ``short``,
|
||||
are implicitly handled via the ordinary C type coercion rules (subject
|
||||
to overflow semantics). Also, no support or hooks are provided for any
|
||||
supplemental "bignum" type add-on packages.
|
||||
|
||||
Depth of nested values
|
||||
----------------------
|
||||
|
||||
To avoid stack exhaustion, Jansson currently limits the nesting depth
|
||||
for arrays and objects to a certain value (default: 2048), defined as
|
||||
a macro ``JSON_PARSER_MAX_DEPTH`` within ``jansson_config.h``.
|
||||
|
||||
The limit is allowed to be set by the RFC; there is no recommended value
|
||||
or required minimum depth to be supported.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<description of the json_object function>
|
||||
|
||||
:copyright: Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
:copyright: Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
:license: MIT, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -55,5 +55,6 @@ def setup(app):
|
||||
app.add_node(refcounting,
|
||||
html=(html_visit, html_depart),
|
||||
latex=(visit, depart),
|
||||
text=(visit, depart))
|
||||
text=(visit, depart),
|
||||
man=(visit, depart))
|
||||
app.add_directive('refcounting', refcounting_directive, 0, (1, 0, 0))
|
||||
|
||||
@@ -30,8 +30,7 @@ compiling and installing is extremely simple::
|
||||
|
||||
To change the destination directory (``/usr/local`` by default), use
|
||||
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
|
||||
--help`` for the list of all possible installation options. (There are
|
||||
no options to customize the resulting Jansson binary.)
|
||||
--help`` for the list of all possible configuration options.
|
||||
|
||||
The command ``make check`` runs the test suite distributed with
|
||||
Jansson. This step is not strictly necessary, but it may find possible
|
||||
@@ -44,7 +43,7 @@ version control. To create the script, the build system needs to be
|
||||
bootstrapped. There are many ways to do this, but the easiest one is
|
||||
to use ``autoreconf``::
|
||||
|
||||
autoreconf -vi
|
||||
autoreconf -fi
|
||||
|
||||
This command creates the ``./configure`` script, which can then be
|
||||
used as described above.
|
||||
@@ -54,22 +53,126 @@ used as described above.
|
||||
.. _libtool: http://www.gnu.org/software/libtool/
|
||||
|
||||
|
||||
.. _build-cmake:
|
||||
|
||||
CMake (various platforms, including Windows)
|
||||
--------------------------------------------
|
||||
|
||||
Jansson can be built using CMake_. Create a build directory for an
|
||||
out-of-tree build, change to that directory, and run ``cmake`` (or ``ccmake``,
|
||||
``cmake-gui``, or similar) to configure the project.
|
||||
|
||||
See the examples below for more detailed information.
|
||||
|
||||
.. note:: In the below examples ``..`` is used as an argument for ``cmake``.
|
||||
This is simply the path to the jansson project root directory.
|
||||
In the example it is assumed you've created a sub-directory ``build``
|
||||
and are using that. You could use any path you want.
|
||||
|
||||
.. _build-cmake-unix:
|
||||
|
||||
Unix (Make files)
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Generating make files on unix:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
|
||||
cd jansson-|release|
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. # or ccmake .. for a GUI.
|
||||
|
||||
Then to build::
|
||||
|
||||
make
|
||||
make check
|
||||
make install
|
||||
|
||||
Windows (Visual Studio)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Creating Visual Studio project files from the command line:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
<unpack>
|
||||
cd jansson-|release|
|
||||
|
||||
md build
|
||||
cd build
|
||||
cmake -G "Visual Studio 10" ..
|
||||
|
||||
You will now have a *Visual Studio Solution* in your build directory.
|
||||
To run the unit tests build the ``RUN_TESTS`` project.
|
||||
|
||||
If you prefer a GUI the ``cmake`` line in the above example can
|
||||
be replaced with::
|
||||
|
||||
cmake-gui ..
|
||||
|
||||
For command line help (including a list of available generators)
|
||||
for CMake_ simply run::
|
||||
|
||||
cmake
|
||||
|
||||
To list available CMake_ settings (and what they are currently set to)
|
||||
for the project, run::
|
||||
|
||||
cmake -LH ..
|
||||
|
||||
Mac OSX (Xcode)
|
||||
^^^^^^^^^^^^^^^
|
||||
If you prefer using Xcode instead of make files on OSX,
|
||||
do the following. (Use the same steps as
|
||||
for :ref:`Unix <build-cmake-unix>`)::
|
||||
|
||||
...
|
||||
cmake -G "Xcode" ..
|
||||
|
||||
Additional CMake settings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Shared library
|
||||
""""""""""""""
|
||||
By default the CMake_ project will generate build files for building the
|
||||
static library. To build the shared version use::
|
||||
|
||||
...
|
||||
cmake -DJANSSON_BUILD_SHARED_LIBS=1 ..
|
||||
|
||||
Changing install directory (same as autoconf --prefix)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Just as with the autoconf_ project you can change the destination directory
|
||||
for ``make install``. The equivalent for autoconfs ``./configure --prefix``
|
||||
in CMake_ is::
|
||||
|
||||
...
|
||||
cmake -DCMAKE_INSTALL_PREFIX:PATH=/some/other/path ..
|
||||
make install
|
||||
|
||||
.. _CMake: http://www.cmake.org
|
||||
|
||||
|
||||
Android
|
||||
-------
|
||||
|
||||
Jansson can be built for Android platforms. Android.mk is in the
|
||||
source root directory. The configuration header file is located in the
|
||||
``android`` directory in the source distribution.
|
||||
|
||||
|
||||
Other Systems
|
||||
-------------
|
||||
|
||||
On Windows and other non Unix-like systems, you may be unable to run
|
||||
the ``./configure`` script. In this case, follow these steps. All the
|
||||
files mentioned can be found in the ``src/`` directory.
|
||||
On non Unix-like systems, you may be unable to run the ``./configure``
|
||||
script. In this case, follow these steps. All the files mentioned can
|
||||
be found in the ``src/`` directory.
|
||||
|
||||
1. Create ``jansson_config.h``. This file has some platform-specific
|
||||
1. Create ``jansson_config.h`` (which has some platform-specific
|
||||
parameters that are normally filled in by the ``./configure``
|
||||
script:
|
||||
|
||||
- On Windows, rename ``jansson_config.h.win32`` to ``jansson_config.h``.
|
||||
|
||||
- On other systems, edit ``jansson_config.h.in``, replacing all
|
||||
``@variable@`` placeholders, and rename the file to
|
||||
``jansson_config.h``.
|
||||
script). Edit ``jansson_config.h.in``, replacing all ``@variable@``
|
||||
placeholders, and rename the file to ``jansson_config.h``.
|
||||
|
||||
2. Make ``jansson.h`` and ``jansson_config.h`` available to the
|
||||
compiler, so that they can be found when compiling programs that
|
||||
@@ -116,7 +219,9 @@ link the program as follows::
|
||||
|
||||
cc -o prog prog.c -ljansson
|
||||
|
||||
Starting from version 1.2, there's also support for pkg-config_::
|
||||
Starting from version 1.2, there's also support for pkg-config_:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cc -o prog prog.c `pkg-config --cflags --libs jansson`
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -51,15 +51,20 @@ static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
|
||||
static char *request(const char *url)
|
||||
{
|
||||
CURL *curl;
|
||||
CURL *curl = NULL;
|
||||
CURLcode status;
|
||||
char *data;
|
||||
struct curl_slist *headers = NULL;
|
||||
char *data = NULL;
|
||||
long code;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl = curl_easy_init();
|
||||
if(!curl)
|
||||
goto error;
|
||||
|
||||
data = malloc(BUFFER_SIZE);
|
||||
if(!curl || !data)
|
||||
return NULL;
|
||||
if(!data)
|
||||
goto error;
|
||||
|
||||
struct write_result write_result = {
|
||||
.data = data,
|
||||
@@ -67,6 +72,11 @@ static char *request(const char *url)
|
||||
};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
/* GitHub commits API v3 requires a User-Agent header */
|
||||
headers = curl_slist_append(headers, "User-Agent: Jansson-Tutorial");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
|
||||
|
||||
@@ -75,23 +85,34 @@ static char *request(const char *url)
|
||||
{
|
||||
fprintf(stderr, "error: unable to request data from %s:\n", url);
|
||||
fprintf(stderr, "%s\n", curl_easy_strerror(status));
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
if(code != 200)
|
||||
{
|
||||
fprintf(stderr, "error: server responded with code %ld\n", code);
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_slist_free_all(headers);
|
||||
curl_global_cleanup();
|
||||
|
||||
/* zero-terminate the result */
|
||||
data[write_result.pos] = '\0';
|
||||
|
||||
return data;
|
||||
|
||||
error:
|
||||
if(data)
|
||||
free(data);
|
||||
if(curl)
|
||||
curl_easy_cleanup(curl);
|
||||
if(headers)
|
||||
curl_slist_free_all(headers);
|
||||
curl_global_cleanup();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -128,6 +149,7 @@ int main(int argc, char *argv[])
|
||||
if(!json_is_array(root))
|
||||
{
|
||||
fprintf(stderr, "error: root is not an array\n");
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -139,28 +161,31 @@ int main(int argc, char *argv[])
|
||||
data = json_array_get(root, i);
|
||||
if(!json_is_object(data))
|
||||
{
|
||||
fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
|
||||
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))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1);
|
||||
fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
commit = json_object_get(data, "commit");
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1);
|
||||
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))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1));
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,33 +2,66 @@
|
||||
Portability
|
||||
***********
|
||||
|
||||
.. _portability-thread-safety:
|
||||
|
||||
Thread safety
|
||||
-------------
|
||||
|
||||
Jansson is thread safe and has no mutable global state. The only
|
||||
exception are the memory allocation functions, that should be set at
|
||||
most once, and only on program startup. See
|
||||
:ref:`apiref-custom-memory-allocation`.
|
||||
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 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
|
||||
==================
|
||||
|
||||
To prevent an attacker from intentionally causing large JSON objects
|
||||
with specially crafted keys to perform very slow, the hash function
|
||||
used by Jansson is randomized using a seed value. The seed is
|
||||
automatically generated on the first explicit or implicit call to
|
||||
:func:`json_object()`, if :func:`json_object_seed()` has not been
|
||||
called beforehand.
|
||||
|
||||
The seed is generated by using operating system's entropy sources if
|
||||
they are available (``/dev/urandom``, ``CryptGenRandom()``). The
|
||||
initialization is done in as thread safe manner as possible, by using
|
||||
architecture specific lockless operations if provided by the platform
|
||||
or the compiler.
|
||||
|
||||
If you're using threads, it's recommended to autoseed the hashtable
|
||||
explicitly before spawning any threads by calling
|
||||
``json_object_seed(0)`` , especially if you're unsure whether the
|
||||
initialization is thread safe on your platform.
|
||||
|
||||
|
||||
Memory allocation functions
|
||||
===========================
|
||||
|
||||
Memory allocation functions should be set at most once, and only on
|
||||
program startup. See :ref:`apiref-custom-memory-allocation`.
|
||||
|
||||
If you want to make sure that two JSON value hierarchies do not
|
||||
contain shared values, use :func:`json_deep_copy()` to make copies.
|
||||
|
||||
Locale
|
||||
------
|
||||
|
||||
@@ -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
|
||||
@@ -178,6 +178,7 @@ We check that the returned value really is an array::
|
||||
if(!json_is_array(root))
|
||||
{
|
||||
fprintf(stderr, "error: root is not an array\n");
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ Then we proceed to loop over all the commits in the array::
|
||||
if(!json_is_object(data))
|
||||
{
|
||||
fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
@@ -205,10 +207,11 @@ Next we'll extract the commit ID (a hexadecimal SHA-1 sum),
|
||||
intermediate commit info object, and the commit message from that
|
||||
object. We also do proper type checks::
|
||||
|
||||
sha = json_object_get(commit, "sha");
|
||||
sha = json_object_get(data, "sha");
|
||||
if(!json_is_string(sha))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -216,6 +219,7 @@ object. We also do proper type checks::
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -223,6 +227,7 @@ object. We also do proper type checks::
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
@@ -233,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);
|
||||
}
|
||||
@@ -251,7 +256,9 @@ For a detailed explanation of reference counting in Jansson, see
|
||||
:ref:`apiref-reference-count` in :ref:`apiref`.
|
||||
|
||||
The program's ready, let's test it and view the latest commits in
|
||||
Jansson's repository::
|
||||
Jansson's repository:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ ./github_commits akheron jansson
|
||||
1581f26a Merge branch '2.3'
|
||||
|
||||
4
examples/README.rst
Normal file
4
examples/README.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
Jansson examples
|
||||
================
|
||||
|
||||
This directory contains simple example programs that use Jansson.
|
||||
203
examples/simple_parse.c
Normal file
203
examples/simple_parse.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Simple example of parsing and printing JSON using jansson.
|
||||
*
|
||||
* SYNOPSIS:
|
||||
* $ examples/simple_parse
|
||||
* Type some JSON > [true, false, null, 1, 0.0, -0.0, "", {"name": "barney"}]
|
||||
* JSON Array of 8 elements:
|
||||
* JSON True
|
||||
* JSON False
|
||||
* JSON Null
|
||||
* JSON Integer: "1"
|
||||
* JSON Real: 0.000000
|
||||
* JSON Real: -0.000000
|
||||
* JSON String: ""
|
||||
* JSON Object of 1 pair:
|
||||
* JSON Key: "name"
|
||||
* JSON String: "barney"
|
||||
*
|
||||
* Copyright (c) 2014 Robert Poor <rdpoor@gmail.com>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <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);
|
||||
void print_json_object(json_t *element, int indent);
|
||||
void print_json_array(json_t *element, int indent);
|
||||
void print_json_string(json_t *element, int indent);
|
||||
void print_json_integer(json_t *element, int indent);
|
||||
void print_json_real(json_t *element, int indent);
|
||||
void print_json_true(json_t *element, int indent);
|
||||
void print_json_false(json_t *element, int indent);
|
||||
void print_json_null(json_t *element, int indent);
|
||||
|
||||
void print_json(json_t *root) {
|
||||
print_json_aux(root, 0);
|
||||
}
|
||||
|
||||
void print_json_aux(json_t *element, int indent) {
|
||||
switch (json_typeof(element)) {
|
||||
case JSON_OBJECT:
|
||||
print_json_object(element, indent);
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
print_json_array(element, indent);
|
||||
break;
|
||||
case JSON_STRING:
|
||||
print_json_string(element, indent);
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
print_json_integer(element, indent);
|
||||
break;
|
||||
case JSON_REAL:
|
||||
print_json_real(element, indent);
|
||||
break;
|
||||
case JSON_TRUE:
|
||||
print_json_true(element, indent);
|
||||
break;
|
||||
case JSON_FALSE:
|
||||
print_json_false(element, indent);
|
||||
break;
|
||||
case JSON_NULL:
|
||||
print_json_null(element, indent);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unrecognized JSON type %d\n", json_typeof(element));
|
||||
}
|
||||
}
|
||||
|
||||
void print_json_indent(int indent) {
|
||||
int i;
|
||||
for (i = 0; i < indent; i++) { putchar(' '); }
|
||||
}
|
||||
|
||||
const char *json_plural(int count) {
|
||||
return count == 1 ? "" : "s";
|
||||
}
|
||||
|
||||
void print_json_object(json_t *element, int indent) {
|
||||
size_t size;
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
print_json_indent(indent);
|
||||
size = json_object_size(element);
|
||||
|
||||
printf("JSON Object of %ld pair%s:\n", size, json_plural(size));
|
||||
json_object_foreach(element, key, value) {
|
||||
print_json_indent(indent + 2);
|
||||
printf("JSON Key: \"%s\"\n", key);
|
||||
print_json_aux(value, indent + 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void print_json_array(json_t *element, int indent) {
|
||||
size_t i;
|
||||
size_t size = json_array_size(element);
|
||||
print_json_indent(indent);
|
||||
|
||||
printf("JSON Array of %ld element%s:\n", size, json_plural(size));
|
||||
for (i = 0; i < size; i++) {
|
||||
print_json_aux(json_array_get(element, i), indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void print_json_string(json_t *element, int indent) {
|
||||
print_json_indent(indent);
|
||||
printf("JSON String: \"%s\"\n", json_string_value(element));
|
||||
}
|
||||
|
||||
void print_json_integer(json_t *element, int indent) {
|
||||
print_json_indent(indent);
|
||||
printf("JSON Integer: \"%" JSON_INTEGER_FORMAT "\"\n", json_integer_value(element));
|
||||
}
|
||||
|
||||
void print_json_real(json_t *element, int indent) {
|
||||
print_json_indent(indent);
|
||||
printf("JSON Real: %f\n", json_real_value(element));
|
||||
}
|
||||
|
||||
void print_json_true(json_t *element, int indent) {
|
||||
(void)element;
|
||||
print_json_indent(indent);
|
||||
printf("JSON True\n");
|
||||
}
|
||||
|
||||
void print_json_false(json_t *element, int indent) {
|
||||
(void)element;
|
||||
print_json_indent(indent);
|
||||
printf("JSON False\n");
|
||||
}
|
||||
|
||||
void print_json_null(json_t *element, int indent) {
|
||||
(void)element;
|
||||
print_json_indent(indent);
|
||||
printf("JSON Null\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse text into a JSON object. If text is valid JSON, returns a
|
||||
* json_t structure, otherwise prints and error and returns null.
|
||||
*/
|
||||
json_t *load_json(const char *text) {
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
|
||||
root = json_loads(text, 0, &error);
|
||||
|
||||
if (root) {
|
||||
return root;
|
||||
} else {
|
||||
fprintf(stderr, "json error on line %d: %s\n", error.line, error.text);
|
||||
return (json_t *)0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a prompt and return (by reference) a null-terminated line of
|
||||
* text. Returns NULL on eof or some error.
|
||||
*/
|
||||
char *read_line(char *line, int max_chars) {
|
||||
printf("Type some JSON > ");
|
||||
fflush(stdout);
|
||||
return fgets(line, max_chars, stdin);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* main
|
||||
*/
|
||||
|
||||
#define MAX_CHARS 4096
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char line[MAX_CHARS];
|
||||
|
||||
if (argc != 1) {
|
||||
fprintf(stderr, "Usage: %s\n", argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
while (read_line(line, MAX_CHARS) != (char *)NULL) {
|
||||
|
||||
/* parse text into JSON structure */
|
||||
json_t *root = load_json(line);
|
||||
|
||||
if (root) {
|
||||
/* print and release the JSON structure */
|
||||
print_json(root);
|
||||
json_decref(root);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
70
release.sh
Executable file
70
release.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Use this script to easily make releases of Jansson. It configures
|
||||
# the source tree, and builds and signs all tarballs.
|
||||
|
||||
die() {
|
||||
echo $1 >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
confirm() {
|
||||
local answer
|
||||
read -p "$1 [yN]: " answer
|
||||
[ "$answer" = "Y" -o "$answer" = "y" ] || exit 0
|
||||
}
|
||||
|
||||
set -e
|
||||
[ -f configure.ac ] || die "Must be run at project root directory"
|
||||
|
||||
# Determine version
|
||||
v=$(grep AC_INIT configure.ac | sed -r 's/.*, \[(.+?)\],.*/\1/')
|
||||
[ -n "$v" ] || die "Unable to determine version"
|
||||
confirm "Version is $v, proceed?"
|
||||
|
||||
# Sanity checks
|
||||
vi=$(grep version-info src/Makefile.am | sed 's/^[ \t]*//g' | cut -d" " -f2)
|
||||
confirm "Libtool version-info is $vi, proceed?"
|
||||
|
||||
r=$(grep 'Released ' CHANGES | head -n 1)
|
||||
confirm "Last CHANGES entry says \"$r\", proceed??"
|
||||
|
||||
dv=$(grep ^version doc/conf.py | sed -r "s/.*'(.*)'.*/\1/")
|
||||
if [ "$dv" != "$v" ]; then
|
||||
die "Documentation version ($dv) doesn't match library version"
|
||||
fi
|
||||
|
||||
[ -f Makefile ] && make distclean || true
|
||||
rm -f jansson-$v.tar.*
|
||||
rm -rf jansson-$v-doc
|
||||
rm -f jansson-$v-doc.tar.*
|
||||
|
||||
autoreconf -fi
|
||||
./configure
|
||||
|
||||
# Run tests and make gz source tarball
|
||||
: ${VALGRIND:=1}
|
||||
export VALGRIND
|
||||
make distcheck
|
||||
|
||||
# Make bzip2 source tarball
|
||||
make dist-bzip2
|
||||
|
||||
# Sign source tarballs
|
||||
for s in gz bz2; do
|
||||
gpg --detach-sign --armor jansson-$v.tar.$s
|
||||
done
|
||||
|
||||
# Build documentation
|
||||
make html
|
||||
mv doc/_build/html jansson-$v-doc
|
||||
|
||||
# Make and sign documentation tarballs
|
||||
for s in gz bz2; do
|
||||
[ $s = gz ] && compress=gzip
|
||||
[ $s = bz2 ] && compress=bzip2
|
||||
tar cf - jansson-$v-doc | $compress -9 -c > jansson-$v-doc.tar.$s
|
||||
gpg --detach-sign --armor jansson-$v-doc.tar.$s
|
||||
done
|
||||
|
||||
echo "All done"
|
||||
@@ -1,6 +1,7 @@
|
||||
EXTRA_DIST = jansson_config.h.win32
|
||||
EXTRA_DIST = jansson.def
|
||||
|
||||
include_HEADERS = jansson.h jansson_config.h
|
||||
include_HEADERS = jansson.h
|
||||
nodist_include_HEADERS = jansson_config.h
|
||||
|
||||
lib_LTLIBRARIES = libjansson.la
|
||||
libjansson_la_SOURCES = \
|
||||
@@ -8,8 +9,10 @@ libjansson_la_SOURCES = \
|
||||
error.c \
|
||||
hashtable.c \
|
||||
hashtable.h \
|
||||
hashtable_seed.c \
|
||||
jansson_private.h \
|
||||
load.c \
|
||||
lookup3.h \
|
||||
memory.c \
|
||||
pack_unpack.c \
|
||||
strbuffer.c \
|
||||
@@ -19,10 +22,6 @@ libjansson_la_SOURCES = \
|
||||
utf.h \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = \
|
||||
-no-undefined \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 7:1:3
|
||||
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
|
||||
endif
|
||||
-version-info 15:0:11
|
||||
|
||||
247
src/dump.c
247
src/dump.c
@@ -1,27 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "jansson_private.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
#include "jansson.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
struct object_key {
|
||||
size_t serial;
|
||||
const char *key;
|
||||
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
|
||||
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
|
||||
|
||||
struct buffer {
|
||||
const size_t size;
|
||||
size_t used;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||
@@ -29,6 +40,17 @@ 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)
|
||||
{
|
||||
struct buffer *buf = (struct buffer *)data;
|
||||
|
||||
if(buf->used + size <= buf->size)
|
||||
memcpy(&buf->data[buf->used], buffer, size);
|
||||
|
||||
buf->used += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
FILE *dest = (FILE *)data;
|
||||
@@ -37,22 +59,36 @@ static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_to_fd(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
int *dest = (int *)data;
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if(write(*dest, buffer, size) == (ssize_t)size)
|
||||
return 0;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 32 spaces (the maximum indentation size) */
|
||||
static char whitespace[] = " ";
|
||||
static const char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
if(FLAGS_TO_INDENT(flags) > 0)
|
||||
{
|
||||
int i, ws_count = JSON_INDENT(flags);
|
||||
unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
|
||||
|
||||
if(dump("\n", 1, data))
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < depth; i++)
|
||||
while(n_spaces > 0)
|
||||
{
|
||||
if(dump(whitespace, ws_count, data))
|
||||
int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
|
||||
|
||||
if(dump(whitespace, cur_n, data))
|
||||
return -1;
|
||||
|
||||
n_spaces -= cur_n;
|
||||
}
|
||||
}
|
||||
else if(space && !(flags & JSON_COMPACT))
|
||||
@@ -62,24 +98,25 @@ static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, int ascii, json_dump_callback_t dump, void *data)
|
||||
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
|
||||
{
|
||||
const char *pos, *end;
|
||||
const char *pos, *end, *lim;
|
||||
int32_t codepoint;
|
||||
|
||||
if(dump("\"", 1, data))
|
||||
return -1;
|
||||
|
||||
end = pos = str;
|
||||
lim = str + len;
|
||||
while(1)
|
||||
{
|
||||
const char *text;
|
||||
char seq[13];
|
||||
int length;
|
||||
|
||||
while(*end)
|
||||
while(end < lim)
|
||||
{
|
||||
end = utf8_iterate(pos, &codepoint);
|
||||
end = utf8_iterate(pos, lim - pos, &codepoint);
|
||||
if(!end)
|
||||
return -1;
|
||||
|
||||
@@ -87,8 +124,12 @@ static int dump_string(const char *str, int ascii, json_dump_callback_t dump, vo
|
||||
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
|
||||
break;
|
||||
|
||||
/* slash */
|
||||
if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
|
||||
break;
|
||||
|
||||
/* non-ASCII */
|
||||
if(ascii && codepoint > 0x7F)
|
||||
if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
|
||||
break;
|
||||
|
||||
pos = end;
|
||||
@@ -102,7 +143,7 @@ static int dump_string(const char *str, int ascii, json_dump_callback_t dump, vo
|
||||
if(end == pos)
|
||||
break;
|
||||
|
||||
/* handle \, ", and control codes */
|
||||
/* handle \, /, ", and control codes */
|
||||
length = 2;
|
||||
switch(codepoint)
|
||||
{
|
||||
@@ -113,12 +154,13 @@ static int dump_string(const char *str, int ascii, json_dump_callback_t dump, vo
|
||||
case '\n': text = "\\n"; break;
|
||||
case '\r': text = "\\r"; break;
|
||||
case '\t': text = "\\t"; break;
|
||||
case '/': text = "\\/"; break;
|
||||
default:
|
||||
{
|
||||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04x", codepoint);
|
||||
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
@@ -131,7 +173,7 @@ static int dump_string(const char *str, int ascii, json_dump_callback_t dump, vo
|
||||
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||
last = 0xDC00 | (codepoint & 0x003ff);
|
||||
|
||||
sprintf(seq, "\\u%04x\\u%04x", first, last);
|
||||
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
|
||||
length = 12;
|
||||
}
|
||||
|
||||
@@ -149,24 +191,29 @@ static int dump_string(const char *str, int ascii, json_dump_callback_t dump, vo
|
||||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
static int object_key_compare_keys(const void *key1, const void *key2)
|
||||
static int compare_keys(const void *key1, const void *key2)
|
||||
{
|
||||
return strcmp(((const struct object_key *)key1)->key,
|
||||
((const struct object_key *)key2)->key);
|
||||
return strcmp(*(const char **)key1, *(const char **)key2);
|
||||
}
|
||||
|
||||
static int object_key_compare_serials(const void *key1, const void *key2)
|
||||
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
|
||||
{
|
||||
size_t a = ((const struct object_key *)key1)->serial;
|
||||
size_t b = ((const struct object_key *)key2)->serial;
|
||||
snprintf(key, key_size, "%p", json);
|
||||
if (hashtable_get(parents, key))
|
||||
return -1;
|
||||
|
||||
return a < b ? -1 : a == b ? 0 : 1;
|
||||
return hashtable_set(parents, key, json_null());
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
json_dump_callback_t dump, void *data)
|
||||
hashtable_t *parents, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
|
||||
int embed = flags & JSON_EMBED;
|
||||
|
||||
flags &= ~JSON_EMBED;
|
||||
|
||||
if(!json)
|
||||
return -1;
|
||||
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_NULL:
|
||||
@@ -186,7 +233,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
|
||||
"%" JSON_INTEGER_FORMAT,
|
||||
json_integer_value(json));
|
||||
if(size >= MAX_INTEGER_STR_LENGTH)
|
||||
if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
return dump(buffer, size, data);
|
||||
@@ -198,7 +245,8 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
int size;
|
||||
double value = json_real_value(json);
|
||||
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
|
||||
FLAGS_TO_PRECISION(flags));
|
||||
if(size < 0)
|
||||
return -1;
|
||||
|
||||
@@ -206,63 +254,59 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), ascii, dump, data);
|
||||
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
json_array_t *array;
|
||||
size_t n;
|
||||
size_t i;
|
||||
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||
char key[2 + (sizeof(json) * 2) + 1];
|
||||
|
||||
/* detect circular references */
|
||||
array = json_to_array(json);
|
||||
if(array->visited)
|
||||
goto array_error;
|
||||
array->visited = 1;
|
||||
if (loop_check(parents, json, key, sizeof(key)))
|
||||
return -1;
|
||||
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
goto array_error;
|
||||
if(!embed && dump("[", 1, data))
|
||||
return -1;
|
||||
if(n == 0) {
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
hashtable_del(parents, key);
|
||||
return embed ? 0 : dump("]", 1, data);
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
goto array_error;
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < n; ++i) {
|
||||
if(do_dump(json_array_get(json, i), flags, depth + 1,
|
||||
dump, data))
|
||||
goto array_error;
|
||||
parents, dump, data))
|
||||
return -1;
|
||||
|
||||
if(i < n - 1)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
goto array_error;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
goto array_error;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
|
||||
array_error:
|
||||
array->visited = 0;
|
||||
return -1;
|
||||
hashtable_del(parents, key);
|
||||
return embed ? 0 : dump("]", 1, data);
|
||||
}
|
||||
|
||||
case JSON_OBJECT:
|
||||
{
|
||||
json_object_t *object;
|
||||
void *iter;
|
||||
const char *separator;
|
||||
int separator_length;
|
||||
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||
char key[2 + (sizeof(json) * 2) + 1];
|
||||
|
||||
if(flags & JSON_COMPACT) {
|
||||
separator = ":";
|
||||
@@ -274,65 +318,56 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
/* detect circular references */
|
||||
object = json_to_object(json);
|
||||
if(object->visited)
|
||||
goto object_error;
|
||||
object->visited = 1;
|
||||
if (loop_check(parents, json, key, sizeof(key)))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
|
||||
if(dump("{", 1, data))
|
||||
goto object_error;
|
||||
if(!embed && dump("{", 1, data))
|
||||
return -1;
|
||||
if(!iter) {
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
hashtable_del(parents, key);
|
||||
return embed ? 0 : dump("}", 1, data);
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
goto object_error;
|
||||
return -1;
|
||||
|
||||
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
|
||||
if(flags & JSON_SORT_KEYS)
|
||||
{
|
||||
struct object_key *keys;
|
||||
const char **keys;
|
||||
size_t size, i;
|
||||
int (*cmp_func)(const void *, const void *);
|
||||
|
||||
size = json_object_size(json);
|
||||
keys = jsonp_malloc(size * sizeof(struct object_key));
|
||||
keys = jsonp_malloc(size * sizeof(const char *));
|
||||
if(!keys)
|
||||
goto object_error;
|
||||
return -1;
|
||||
|
||||
i = 0;
|
||||
while(iter)
|
||||
{
|
||||
keys[i].serial = hashtable_iter_serial(iter);
|
||||
keys[i].key = json_object_iter_key(iter);
|
||||
keys[i] = json_object_iter_key(iter);
|
||||
iter = json_object_iter_next((json_t *)json, iter);
|
||||
i++;
|
||||
}
|
||||
assert(i == size);
|
||||
|
||||
if(flags & JSON_SORT_KEYS)
|
||||
cmp_func = object_key_compare_keys;
|
||||
else
|
||||
cmp_func = object_key_compare_serials;
|
||||
|
||||
qsort(keys, size, sizeof(struct object_key), cmp_func);
|
||||
qsort(keys, size, sizeof(const char *), compare_keys);
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = keys[i].key;
|
||||
key = keys[i];
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, ascii, dump, data);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
do_dump(value, flags, depth + 1, parents, dump, data))
|
||||
{
|
||||
jsonp_free(keys);
|
||||
goto object_error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(i < size - 1)
|
||||
@@ -341,7 +376,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
{
|
||||
jsonp_free(keys);
|
||||
goto object_error;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -349,7 +384,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
{
|
||||
jsonp_free(keys);
|
||||
goto object_error;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,35 +398,32 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
const char *key = json_object_iter_key(iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), ascii, dump, data);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
goto object_error;
|
||||
parents, dump, data))
|
||||
return -1;
|
||||
|
||||
if(next)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
goto object_error;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
goto object_error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
|
||||
object_error:
|
||||
object->visited = 0;
|
||||
return -1;
|
||||
hashtable_del(parents, key);
|
||||
return embed ? 0 : dump("}", 1, data);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -417,11 +449,26 @@ 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 };
|
||||
|
||||
if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
|
||||
return 0;
|
||||
|
||||
return buf.used;
|
||||
}
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
{
|
||||
return json_dump_callback(json, dump_to_file, (void *)output, flags);
|
||||
}
|
||||
|
||||
int json_dumpfd(const json_t *json, int output, size_t flags)
|
||||
{
|
||||
return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
|
||||
}
|
||||
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
{
|
||||
int result;
|
||||
@@ -432,16 +479,26 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
|
||||
result = json_dumpf(json, output, flags);
|
||||
|
||||
fclose(output);
|
||||
if(fclose(output) != 0)
|
||||
return -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
||||
{
|
||||
int res;
|
||||
hashtable_t parents_set;
|
||||
|
||||
if(!(flags & JSON_ENCODE_ANY)) {
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return do_dump(json, flags, 0, callback, data);
|
||||
if (hashtable_init(&parents_set))
|
||||
return -1;
|
||||
res = do_dump(json, flags, 0, &parents_set, callback, data);
|
||||
hashtable_close(&parents_set);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
20
src/error.c
20
src/error.c
@@ -25,26 +25,28 @@ void jsonp_error_set_source(json_error_t *error, const char *source)
|
||||
|
||||
length = strlen(source);
|
||||
if(length < JSON_ERROR_SOURCE_LENGTH)
|
||||
strcpy(error->source, source);
|
||||
strncpy(error->source, source, length + 1);
|
||||
else {
|
||||
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
|
||||
strcpy(error->source, "...");
|
||||
strcpy(error->source + 3, source + extra);
|
||||
strncpy(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, ...)
|
||||
size_t position, enum json_error_code code,
|
||||
const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg);
|
||||
jsonp_error_vset(error, line, column, position, msg, ap);
|
||||
jsonp_error_vset(error, line, column, position, code, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
size_t position, const char *msg, va_list ap)
|
||||
size_t position, enum json_error_code code,
|
||||
const char *msg, va_list ap)
|
||||
{
|
||||
if(!error)
|
||||
return;
|
||||
@@ -56,7 +58,9 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
|
||||
error->line = line;
|
||||
error->column = column;
|
||||
error->position = position;
|
||||
error->position = (int)position;
|
||||
|
||||
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
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;
|
||||
}
|
||||
|
||||
128
src/hashtable.c
128
src/hashtable.c
@@ -1,38 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <jansson_config.h> /* for JSON_INLINE */
|
||||
#include "jansson_private.h" /* for container_of() */
|
||||
#include "hashtable.h"
|
||||
|
||||
#ifndef INITIAL_HASHTABLE_ORDER
|
||||
#define INITIAL_HASHTABLE_ORDER 3
|
||||
#endif
|
||||
|
||||
typedef struct hashtable_list list_t;
|
||||
typedef struct hashtable_pair pair_t;
|
||||
typedef struct hashtable_bucket bucket_t;
|
||||
|
||||
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)
|
||||
|
||||
/* From http://www.cse.yorku.ca/~oz/hash.html */
|
||||
static size_t hash_str(const void *ptr)
|
||||
{
|
||||
const char *str = (const char *)ptr;
|
||||
|
||||
size_t hash = 5381;
|
||||
size_t c;
|
||||
|
||||
while((c = (size_t)*str))
|
||||
{
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
str++;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
#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))
|
||||
|
||||
static JSON_INLINE void list_init(list_t *list)
|
||||
{
|
||||
@@ -74,19 +77,6 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
|
||||
}
|
||||
}
|
||||
|
||||
static size_t primes[] = {
|
||||
5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
|
||||
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
|
||||
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
|
||||
805306457, 1610612741
|
||||
};
|
||||
|
||||
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
|
||||
{
|
||||
return primes[hashtable->num_buckets];
|
||||
}
|
||||
|
||||
|
||||
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
const char *key, size_t hash)
|
||||
{
|
||||
@@ -120,7 +110,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
bucket_t *bucket;
|
||||
size_t index;
|
||||
|
||||
index = hash % num_buckets(hashtable);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
@@ -137,6 +127,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
bucket->last = pair->list.prev;
|
||||
|
||||
list_remove(&pair->list);
|
||||
list_remove(&pair->ordered_list);
|
||||
json_decref(pair->value);
|
||||
|
||||
jsonp_free(pair);
|
||||
@@ -163,18 +154,21 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
size_t i, index, new_size;
|
||||
size_t i, index, new_size, new_order;
|
||||
struct hashtable_bucket *new_buckets;
|
||||
|
||||
jsonp_free(hashtable->buckets);
|
||||
new_order = hashtable->order + 1;
|
||||
new_size = hashsize(new_order);
|
||||
|
||||
hashtable->num_buckets++;
|
||||
new_size = num_buckets(hashtable);
|
||||
|
||||
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!new_buckets)
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
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;
|
||||
@@ -199,14 +193,15 @@ int hashtable_init(hashtable_t *hashtable)
|
||||
size_t i;
|
||||
|
||||
hashtable->size = 0;
|
||||
hashtable->num_buckets = 0; /* index to primes[] */
|
||||
hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
|
||||
hashtable->order = INITIAL_HASHTABLE_ORDER;
|
||||
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
list_init(&hashtable->list);
|
||||
list_init(&hashtable->ordered_list);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
@@ -221,21 +216,19 @@ void hashtable_close(hashtable_t *hashtable)
|
||||
jsonp_free(hashtable->buckets);
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value)
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
|
||||
{
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
size_t hash, index;
|
||||
|
||||
/* rehash if the load ratio exceeds 1 */
|
||||
if(hashtable->size >= num_buckets(hashtable))
|
||||
if(hashtable->size >= hashsize(hashtable->order))
|
||||
if(hashtable_do_rehash(hashtable))
|
||||
return -1;
|
||||
|
||||
hash = hash_str(key);
|
||||
index = hash % num_buckets(hashtable);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
|
||||
@@ -249,17 +242,25 @@ int hashtable_set(hashtable_t *hashtable,
|
||||
/* offsetof(...) returns the size of pair_t without the last,
|
||||
flexible member. This way, the correct amount is
|
||||
allocated. */
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1);
|
||||
|
||||
size_t len = strlen(key);
|
||||
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
|
||||
/* Avoid an overflow if the key is very long */
|
||||
return -1;
|
||||
}
|
||||
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
|
||||
if(!pair)
|
||||
return -1;
|
||||
|
||||
pair->hash = hash;
|
||||
pair->serial = serial;
|
||||
strcpy(pair->key, key);
|
||||
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);
|
||||
|
||||
hashtable->size++;
|
||||
}
|
||||
@@ -273,7 +274,7 @@ void *hashtable_get(hashtable_t *hashtable, const char *key)
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
@@ -294,19 +295,20 @@ void hashtable_clear(hashtable_t *hashtable)
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
}
|
||||
|
||||
list_init(&hashtable->list);
|
||||
list_init(&hashtable->ordered_list);
|
||||
hashtable->size = 0;
|
||||
}
|
||||
|
||||
void *hashtable_iter(hashtable_t *hashtable)
|
||||
{
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
@@ -316,44 +318,38 @@ void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
return NULL;
|
||||
|
||||
return &pair->list;
|
||||
return &pair->ordered_list;
|
||||
}
|
||||
|
||||
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
|
||||
{
|
||||
list_t *list = (list_t *)iter;
|
||||
if(list->next == &hashtable->list)
|
||||
if(list->next == &hashtable->ordered_list)
|
||||
return NULL;
|
||||
return list->next;
|
||||
}
|
||||
|
||||
void *hashtable_iter_key(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->key;
|
||||
}
|
||||
|
||||
size_t hashtable_iter_serial(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
return pair->serial;
|
||||
}
|
||||
|
||||
void *hashtable_iter_value(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void hashtable_iter_set(void *iter, json_t *value)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
|
||||
json_decref(pair->value);
|
||||
pair->value = value;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifndef HASHTABLE_H
|
||||
#define HASHTABLE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "jansson.h"
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
@@ -17,10 +20,10 @@ struct hashtable_list {
|
||||
key-value pair. In this case, it just encodes some extra data,
|
||||
too */
|
||||
struct hashtable_pair {
|
||||
size_t hash;
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
size_t hash;
|
||||
json_t *value;
|
||||
size_t serial;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
@@ -32,13 +35,15 @@ struct hashtable_bucket {
|
||||
typedef struct hashtable {
|
||||
size_t size;
|
||||
struct hashtable_bucket *buckets;
|
||||
size_t num_buckets; /* index to primes[] */
|
||||
size_t order; /* hashtable has pow(2, order) buckets */
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
} hashtable_t;
|
||||
|
||||
|
||||
#define hashtable_key_to_iter(key_) \
|
||||
(&(container_of(key_, struct hashtable_pair, key)->list))
|
||||
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
|
||||
|
||||
|
||||
/**
|
||||
* hashtable_init - Initialize a hashtable object
|
||||
@@ -76,9 +81,7 @@ void hashtable_close(hashtable_t *hashtable);
|
||||
*
|
||||
* Returns 0 on success, -1 on failure (out of memory).
|
||||
*/
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value);
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
|
||||
|
||||
/**
|
||||
* hashtable_get - Get a value associated with a key
|
||||
@@ -155,13 +158,6 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
|
||||
*/
|
||||
void *hashtable_iter_key(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_serial - Retrieve the serial number pointed to by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
*/
|
||||
size_t hashtable_iter_serial(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_value - Retrieve the value pointed by an iterator
|
||||
*
|
||||
|
||||
277
src/hashtable_seed.c
Normal file
277
src/hashtable_seed.c
Normal file
@@ -0,0 +1,277 @@
|
||||
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
|
||||
the hash function.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SCHED_H
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
|
||||
|
||||
static uint32_t buf_to_uint32(char *data) {
|
||||
size_t i;
|
||||
uint32_t result = 0;
|
||||
|
||||
for (i = 0; i < sizeof(uint32_t); i++)
|
||||
result = (result << 8) | (unsigned char)data[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* /dev/urandom */
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
static int seed_from_urandom(uint32_t *seed) {
|
||||
/* Use unbuffered I/O if we have open(), close() and read(). Otherwise
|
||||
fall back to fopen() */
|
||||
|
||||
char data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ)
|
||||
int urandom;
|
||||
urandom = open("/dev/urandom", O_RDONLY);
|
||||
if (urandom == -1)
|
||||
return 1;
|
||||
|
||||
ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t);
|
||||
close(urandom);
|
||||
#else
|
||||
FILE *urandom;
|
||||
|
||||
urandom = fopen("/dev/urandom", "rb");
|
||||
if (!urandom)
|
||||
return 1;
|
||||
|
||||
ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t);
|
||||
fclose(urandom);
|
||||
#endif
|
||||
|
||||
if (!ok)
|
||||
return 1;
|
||||
|
||||
*seed = buf_to_uint32(data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Windows Crypto API */
|
||||
#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);
|
||||
|
||||
static int seed_from_windows_cryptoapi(uint32_t *seed)
|
||||
{
|
||||
HINSTANCE hAdvAPI32 = NULL;
|
||||
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
|
||||
CRYPTGENRANDOM pCryptGenRandom = NULL;
|
||||
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
|
||||
HCRYPTPROV hCryptProv = 0;
|
||||
BYTE data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
|
||||
if(hAdvAPI32 == NULL)
|
||||
return 1;
|
||||
|
||||
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
|
||||
if (!pCryptAcquireContext)
|
||||
return 1;
|
||||
|
||||
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom");
|
||||
if (!pCryptGenRandom)
|
||||
return 1;
|
||||
|
||||
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
|
||||
if (!pCryptReleaseContext)
|
||||
return 1;
|
||||
|
||||
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||
return 1;
|
||||
|
||||
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
|
||||
pCryptReleaseContext(hCryptProv, 0);
|
||||
|
||||
if (!ok)
|
||||
return 1;
|
||||
|
||||
*seed = buf_to_uint32((char *)data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* gettimeofday() and getpid() */
|
||||
static int seed_from_timestamp_and_pid(uint32_t *seed) {
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
/* XOR of seconds and microseconds */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
*seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec;
|
||||
#else
|
||||
/* Seconds only */
|
||||
*seed = (uint32_t)time(NULL);
|
||||
#endif
|
||||
|
||||
/* XOR with PID for more randomness */
|
||||
#if defined(_WIN32)
|
||||
*seed ^= (uint32_t)GetCurrentProcessId();
|
||||
#elif defined(HAVE_GETPID)
|
||||
*seed ^= (uint32_t)getpid();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t generate_seed() {
|
||||
uint32_t seed;
|
||||
int done = 0;
|
||||
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
if (seed_from_urandom(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
if (seed_from_windows_cryptoapi(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
if (!done) {
|
||||
/* Fall back to timestamp and PID if no better randomness is
|
||||
available */
|
||||
seed_from_timestamp_and_pid(&seed);
|
||||
}
|
||||
|
||||
/* Make sure the seed is never zero */
|
||||
if (seed == 0)
|
||||
seed = 1;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
volatile uint32_t hashtable_seed = 0;
|
||||
|
||||
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||
static volatile char seed_initialized = 0;
|
||||
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) {
|
||||
/* Do the seeding ourselves */
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
__atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE);
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
do {
|
||||
#ifdef HAVE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#endif
|
||||
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (new_seed == 0) {
|
||||
/* Explicit synchronization fences are not supported by the
|
||||
__sync builtins, so every thread getting here has to
|
||||
generate the seed value.
|
||||
*/
|
||||
new_seed = generate_seed();
|
||||
}
|
||||
|
||||
do {
|
||||
if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) {
|
||||
/* We were the first to seed */
|
||||
break;
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
#ifdef HAVE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
} while(hashtable_seed == 0);
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
static long seed_initialized = 0;
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (InterlockedIncrement(&seed_initialized) == 1) {
|
||||
/* Do the seeding ourselves */
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
hashtable_seed = new_seed;
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
do {
|
||||
SwitchToThread();
|
||||
} while (hashtable_seed == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Fall back to a thread-unsafe version */
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
hashtable_seed = new_seed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
75
src/jansson.def
Normal file
75
src/jansson.def
Normal file
@@ -0,0 +1,75 @@
|
||||
EXPORTS
|
||||
json_delete
|
||||
json_true
|
||||
json_false
|
||||
json_null
|
||||
json_sprintf
|
||||
json_vsprintf
|
||||
json_string
|
||||
json_stringn
|
||||
json_string_nocheck
|
||||
json_stringn_nocheck
|
||||
json_string_value
|
||||
json_string_length
|
||||
json_string_set
|
||||
json_string_setn
|
||||
json_string_set_nocheck
|
||||
json_string_setn_nocheck
|
||||
json_integer
|
||||
json_integer_value
|
||||
json_integer_set
|
||||
json_real
|
||||
json_real_value
|
||||
json_real_set
|
||||
json_number_value
|
||||
json_array
|
||||
json_array_size
|
||||
json_array_get
|
||||
json_array_set_new
|
||||
json_array_append_new
|
||||
json_array_insert_new
|
||||
json_array_remove
|
||||
json_array_clear
|
||||
json_array_extend
|
||||
json_object
|
||||
json_object_size
|
||||
json_object_get
|
||||
json_object_set_new
|
||||
json_object_set_new_nocheck
|
||||
json_object_del
|
||||
json_object_clear
|
||||
json_object_update
|
||||
json_object_update_existing
|
||||
json_object_update_missing
|
||||
json_object_iter
|
||||
json_object_iter_at
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_object_key_to_iter
|
||||
json_object_seed
|
||||
json_dumps
|
||||
json_dumpb
|
||||
json_dumpf
|
||||
json_dumpfd
|
||||
json_dump_file
|
||||
json_dump_callback
|
||||
json_loads
|
||||
json_loadb
|
||||
json_loadf
|
||||
json_loadfd
|
||||
json_load_file
|
||||
json_load_callback
|
||||
json_equal
|
||||
json_copy
|
||||
json_deep_copy
|
||||
json_pack
|
||||
json_pack_ex
|
||||
json_vpack_ex
|
||||
json_unpack
|
||||
json_unpack_ex
|
||||
json_vunpack_ex
|
||||
json_set_alloc_funcs
|
||||
json_get_alloc_funcs
|
||||
|
||||
161
src/jansson.h
161
src/jansson.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <stdlib.h> /* for size_t */
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <jansson_config.h>
|
||||
#include "jansson_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -21,11 +21,11 @@ extern "C" {
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 3
|
||||
#define JANSSON_MICRO_VERSION 1
|
||||
#define JANSSON_MINOR_VERSION 11
|
||||
#define JANSSON_MICRO_VERSION 0
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.3.1"
|
||||
#define JANSSON_VERSION "2.11"
|
||||
|
||||
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
|
||||
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
|
||||
@@ -33,6 +33,11 @@ extern "C" {
|
||||
(JANSSON_MINOR_VERSION << 8) | \
|
||||
(JANSSON_MICRO_VERSION << 0))
|
||||
|
||||
/* If __atomic or __sync builtins are available the library is thread
|
||||
* safe for all read-only functions plus reference counting. */
|
||||
#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS
|
||||
#define JANSSON_THREAD_SAFE_REFCOUNT 1
|
||||
#endif
|
||||
|
||||
/* types */
|
||||
|
||||
@@ -47,11 +52,12 @@ typedef enum {
|
||||
JSON_NULL
|
||||
} json_type;
|
||||
|
||||
typedef struct {
|
||||
typedef struct json_t {
|
||||
json_type type;
|
||||
size_t refcount;
|
||||
volatile size_t refcount;
|
||||
} json_t;
|
||||
|
||||
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
|
||||
#if JSON_INTEGER_IS_LONG_LONG
|
||||
#ifdef _WIN32
|
||||
#define JSON_INTEGER_FORMAT "I64d"
|
||||
@@ -63,36 +69,53 @@ typedef long long json_int_t;
|
||||
#define JSON_INTEGER_FORMAT "ld"
|
||||
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_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_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_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
|
||||
|
||||
/* construction, destruction, reference counting */
|
||||
|
||||
json_t *json_object(void);
|
||||
json_t *json_array(void);
|
||||
json_t *json_string(const char *value);
|
||||
json_t *json_stringn(const char *value, size_t len);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len);
|
||||
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())
|
||||
json_t *json_null(void);
|
||||
|
||||
/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */
|
||||
#if JSON_HAVE_ATOMIC_BUILTINS
|
||||
#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE)
|
||||
#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE)
|
||||
#elif JSON_HAVE_SYNC_BUILTINS
|
||||
#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1)
|
||||
#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1)
|
||||
#else
|
||||
#define JSON_INTERNAL_INCREF(json) (++json->refcount)
|
||||
#define JSON_INTERNAL_DECREF(json) (--json->refcount)
|
||||
#endif
|
||||
|
||||
static JSON_INLINE
|
||||
json_t *json_incref(json_t *json)
|
||||
{
|
||||
if(json && json->refcount != (size_t)-1)
|
||||
++json->refcount;
|
||||
JSON_INTERNAL_INCREF(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -102,17 +125,30 @@ void json_delete(json_t *json);
|
||||
static JSON_INLINE
|
||||
void json_decref(json_t *json)
|
||||
{
|
||||
if(json && json->refcount != (size_t)-1 && --json->refcount == 0)
|
||||
if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0)
|
||||
json_delete(json);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
static JSON_INLINE
|
||||
void json_decrefp(json_t **json)
|
||||
{
|
||||
if(json) {
|
||||
json_decref(*json);
|
||||
*json = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define json_auto_t json_t __attribute__((cleanup(json_decrefp)))
|
||||
#endif
|
||||
|
||||
|
||||
/* error reporting */
|
||||
|
||||
#define JSON_ERROR_TEXT_LENGTH 160
|
||||
#define JSON_ERROR_SOURCE_LENGTH 80
|
||||
|
||||
typedef struct {
|
||||
typedef struct json_error_t {
|
||||
int line;
|
||||
int column;
|
||||
int position;
|
||||
@@ -120,9 +156,34 @@ 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);
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
@@ -145,6 +206,18 @@ int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
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_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)
|
||||
{
|
||||
@@ -173,9 +246,9 @@ 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 index, json_t *value)
|
||||
int json_array_set(json_t *array, size_t ind, json_t *value)
|
||||
{
|
||||
return json_array_set_new(array, index, json_incref(value));
|
||||
return json_array_set_new(array, ind, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE
|
||||
@@ -185,22 +258,24 @@ int json_array_append(json_t *array, json_t *value)
|
||||
}
|
||||
|
||||
static JSON_INLINE
|
||||
int json_array_insert(json_t *array, size_t index, json_t *value)
|
||||
int json_array_insert(json_t *array, size_t ind, json_t *value)
|
||||
{
|
||||
return json_array_insert_new(array, index, json_incref(value));
|
||||
return json_array_insert_new(array, ind, json_incref(value));
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
size_t json_string_length(const json_t *string);
|
||||
json_int_t json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(json_t *string, const char *value);
|
||||
int json_string_setn(json_t *string, const char *value, size_t len);
|
||||
int json_string_set_nocheck(json_t *string, const char *value);
|
||||
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
|
||||
int json_integer_set(json_t *integer, json_int_t value);
|
||||
int json_real_set(json_t *real, double value);
|
||||
|
||||
|
||||
/* pack, unpack */
|
||||
|
||||
json_t *json_pack(const char *fmt, ...);
|
||||
@@ -214,43 +289,60 @@ int json_unpack(json_t *root, const char *fmt, ...);
|
||||
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
|
||||
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
|
||||
|
||||
/* sprintf */
|
||||
|
||||
json_t *json_sprintf(const char *fmt, ...);
|
||||
json_t *json_vsprintf(const char *fmt, va_list ap);
|
||||
|
||||
|
||||
/* equality */
|
||||
|
||||
int json_equal(json_t *value1, json_t *value2);
|
||||
int json_equal(const json_t *value1, const json_t *value2);
|
||||
|
||||
|
||||
/* copying */
|
||||
|
||||
json_t *json_copy(json_t *value);
|
||||
json_t *json_deep_copy(json_t *value);
|
||||
json_t *json_deep_copy(const json_t *value);
|
||||
|
||||
|
||||
/* decoding */
|
||||
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
#define JSON_DECODE_ANY 0x4
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
#define JSON_DECODE_ANY 0x4
|
||||
#define JSON_DECODE_INT_AS_REAL 0x8
|
||||
#define JSON_ALLOW_NUL 0x10
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
#define JSON_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);
|
||||
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);
|
||||
|
||||
@@ -260,6 +352,7 @@ typedef void *(*json_malloc_t)(size_t);
|
||||
typedef void (*json_free_t)(void *);
|
||||
|
||||
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
|
||||
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -36,4 +36,16 @@
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
|
||||
|
||||
/* If __atomic builtins are available they will be used to manage
|
||||
reference counts of json_t. */
|
||||
#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@
|
||||
|
||||
/* If __atomic builtins are not available we try using __sync builtins
|
||||
to manage reference counts of json_t. */
|
||||
#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@
|
||||
|
||||
/* Maximum recursion depth for parsing JSON input.
|
||||
This limits the depth of e.g. array-within-array constructions. */
|
||||
#define JSON_PARSER_MAX_DEPTH 2048
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef JANSSON_PRIVATE_H
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
#include "jansson_private_config.h"
|
||||
#include <stddef.h>
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
@@ -34,8 +35,6 @@
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
size_t serial;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -43,12 +42,12 @@ typedef struct {
|
||||
size_t size;
|
||||
size_t entries;
|
||||
json_t **table;
|
||||
int visited;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
size_t length;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -64,23 +63,47 @@ typedef struct {
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
/* Create a string by taking ownership of an existing buffer */
|
||||
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, ...);
|
||||
size_t position, enum json_error_code code,
|
||||
const char *msg, ...);
|
||||
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
size_t position, const char *msg, va_list ap);
|
||||
size_t position, enum json_error_code code,
|
||||
const char *msg, va_list ap);
|
||||
|
||||
/* Locale independent string<->double conversions */
|
||||
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value);
|
||||
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_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);
|
||||
|
||||
|
||||
/* 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
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
349
src/load.c
349
src/load.c
@@ -1,20 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "jansson_private.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
#include "jansson.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
@@ -37,7 +44,7 @@
|
||||
#define l_isalpha(c) (l_isupper(c) || l_islower(c))
|
||||
#define l_isdigit(c) ('0' <= (c) && (c) <= '9')
|
||||
#define l_isxdigit(c) \
|
||||
(l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f')
|
||||
(l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f'))
|
||||
|
||||
/* Read one byte from stream, convert to unsigned char, then int, and
|
||||
return. return EOF on end of file. This corresponds to the
|
||||
@@ -58,9 +65,14 @@ typedef struct {
|
||||
typedef struct {
|
||||
stream_t stream;
|
||||
strbuffer_t saved_text;
|
||||
size_t flags;
|
||||
size_t depth;
|
||||
int token;
|
||||
union {
|
||||
char *string;
|
||||
struct {
|
||||
char *val;
|
||||
size_t len;
|
||||
} string;
|
||||
json_int_t integer;
|
||||
double real;
|
||||
} value;
|
||||
@@ -72,6 +84,7 @@ typedef struct {
|
||||
/*** error reporting ***/
|
||||
|
||||
static void error_set(json_error_t *error, const lex_t *lex,
|
||||
enum json_error_code code,
|
||||
const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
@@ -87,6 +100,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
if(lex)
|
||||
@@ -102,11 +116,16 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
||||
if(lex->saved_text.length <= 20) {
|
||||
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near '%s'", msg_text, saved_text);
|
||||
msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
||||
result = msg_with_context;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(code == json_error_invalid_syntax) {
|
||||
/* More specific error code for premature end of file. */
|
||||
code = json_error_premature_end_of_input;
|
||||
}
|
||||
if(lex->stream.state == STREAM_STATE_ERROR) {
|
||||
/* No context for UTF-8 decoding errors */
|
||||
result = msg_text;
|
||||
@@ -114,12 +133,13 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
||||
else {
|
||||
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near end of file", msg_text);
|
||||
msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
||||
result = msg_with_context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsonp_error_set(error, line, col, pos, "%s", result);
|
||||
jsonp_error_set(error, line, col, pos, code, "%s", result);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +180,7 @@ static int stream_get(stream_t *stream, json_error_t *error)
|
||||
if(0x80 <= c && c <= 0xFF)
|
||||
{
|
||||
/* multi-byte UTF-8 sequence */
|
||||
int i, count;
|
||||
size_t i, count;
|
||||
|
||||
count = utf8_check_first(c);
|
||||
if(!count)
|
||||
@@ -198,7 +218,7 @@ static int stream_get(stream_t *stream, json_error_t *error)
|
||||
|
||||
out:
|
||||
stream->state = STREAM_STATE_ERROR;
|
||||
error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c);
|
||||
error_set(error, stream_to_lex(stream), json_error_invalid_utf8, "unable to decode byte 0x%x", c);
|
||||
return STREAM_STATE_ERROR;
|
||||
}
|
||||
|
||||
@@ -247,9 +267,18 @@ static void lex_unget(lex_t *lex, int c)
|
||||
static void lex_unget_unsave(lex_t *lex, int c)
|
||||
{
|
||||
if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
|
||||
/* Since we treat warnings as errors, when assertions are turned
|
||||
* off the "d" variable would be set but never used. Which is
|
||||
* treated as an error by GCC.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
char d;
|
||||
#endif
|
||||
stream_unget(&lex->stream, c);
|
||||
d = strbuffer_pop(&lex->saved_text);
|
||||
#ifndef NDEBUG
|
||||
d =
|
||||
#endif
|
||||
strbuffer_pop(&lex->saved_text);
|
||||
assert(c == d);
|
||||
}
|
||||
}
|
||||
@@ -264,6 +293,13 @@ static void lex_save_cached(lex_t *lex)
|
||||
}
|
||||
}
|
||||
|
||||
static void lex_free_string(lex_t *lex)
|
||||
{
|
||||
jsonp_free(lex->value.string.val);
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
|
||||
/* assumes that str points to 'u' plus at least 4 valid hex digits */
|
||||
static int32_t decode_unicode_escape(const char *str)
|
||||
{
|
||||
@@ -282,7 +318,7 @@ static int32_t decode_unicode_escape(const char *str)
|
||||
else if(l_isupper(c))
|
||||
value += c - 'A' + 10;
|
||||
else
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -295,7 +331,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
char *t;
|
||||
int i;
|
||||
|
||||
lex->value.string = NULL;
|
||||
lex->value.string.val = NULL;
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
@@ -305,7 +341,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
goto out;
|
||||
|
||||
else if(c == STREAM_STATE_EOF) {
|
||||
error_set(error, lex, "premature end of input");
|
||||
error_set(error, lex, json_error_premature_end_of_input, "premature end of input");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -313,9 +349,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
/* control character */
|
||||
lex_unget_unsave(lex, c);
|
||||
if(c == '\n')
|
||||
error_set(error, lex, "unexpected newline", c);
|
||||
error_set(error, lex, json_error_invalid_syntax, "unexpected newline");
|
||||
else
|
||||
error_set(error, lex, "control character 0x%x", c);
|
||||
error_set(error, lex, json_error_invalid_syntax, "control character 0x%x", c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -325,7 +361,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
c = lex_get_save(lex, error);
|
||||
for(i = 0; i < 4; i++) {
|
||||
if(!l_isxdigit(c)) {
|
||||
error_set(error, lex, "invalid escape");
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid escape");
|
||||
goto out;
|
||||
}
|
||||
c = lex_get_save(lex, error);
|
||||
@@ -335,7 +371,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
c == 'f' || c == 'n' || c == 'r' || c == 't')
|
||||
c = lex_get_save(lex, error);
|
||||
else {
|
||||
error_set(error, lex, "invalid escape");
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid escape");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -350,14 +386,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
|
||||
are converted to 4 bytes
|
||||
*/
|
||||
lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!lex->value.string) {
|
||||
t = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!t) {
|
||||
/* this is not very nice, since TOKEN_INVALID is returned */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the target */
|
||||
t = lex->value.string;
|
||||
lex->value.string.val = t;
|
||||
|
||||
/* + 1 to skip the " */
|
||||
p = strbuffer_value(&lex->saved_text) + 1;
|
||||
@@ -366,17 +400,24 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(*p == '\\') {
|
||||
p++;
|
||||
if(*p == 'u') {
|
||||
char buffer[4];
|
||||
int length;
|
||||
size_t length;
|
||||
int32_t value;
|
||||
|
||||
value = decode_unicode_escape(p);
|
||||
if(value < 0) {
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xD800 <= value && value <= 0xDBFF) {
|
||||
/* surrogate pair */
|
||||
if(*p == '\\' && *(p + 1) == 'u') {
|
||||
int32_t value2 = decode_unicode_escape(++p);
|
||||
if(value2 < 0) {
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
@@ -389,6 +430,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
else {
|
||||
/* invalid second surrogate */
|
||||
error_set(error, lex,
|
||||
json_error_invalid_syntax,
|
||||
"invalid Unicode '\\u%04X\\u%04X'",
|
||||
value, value2);
|
||||
goto out;
|
||||
@@ -396,25 +438,18 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
}
|
||||
else {
|
||||
/* no second surrogate */
|
||||
error_set(error, lex, "invalid Unicode '\\u%04X'",
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'",
|
||||
value);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if(0xDC00 <= value && value <= 0xDFFF) {
|
||||
error_set(error, lex, "invalid Unicode '\\u%04X'", value);
|
||||
goto out;
|
||||
}
|
||||
else if(value == 0)
|
||||
{
|
||||
error_set(error, lex, "\\u0000 is not allowed");
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", value);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(utf8_encode(value, buffer, &length))
|
||||
if(utf8_encode(value, t, &length))
|
||||
assert(0);
|
||||
|
||||
memcpy(t, buffer, length);
|
||||
t += length;
|
||||
}
|
||||
else {
|
||||
@@ -436,24 +471,31 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
*(t++) = *(p++);
|
||||
}
|
||||
*t = '\0';
|
||||
lex->value.string.len = t - lex->value.string.val;
|
||||
lex->token = TOKEN_STRING;
|
||||
return;
|
||||
|
||||
out:
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
}
|
||||
|
||||
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
|
||||
#if JSON_INTEGER_IS_LONG_LONG
|
||||
#ifdef _MSC_VER /* Microsoft Visual Studio */
|
||||
#define json_strtoint _strtoi64
|
||||
#else
|
||||
#define json_strtoint strtoll
|
||||
#endif
|
||||
#else
|
||||
#define json_strtoint strtol
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
{
|
||||
const char *saved_text;
|
||||
char *end;
|
||||
double value;
|
||||
double doubleval;
|
||||
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
@@ -468,36 +510,38 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
}
|
||||
}
|
||||
else if(l_isdigit(c)) {
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c));
|
||||
}
|
||||
else {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(c != '.' && c != 'E' && c != 'e') {
|
||||
json_int_t value;
|
||||
if(!(lex->flags & JSON_DECODE_INT_AS_REAL) &&
|
||||
c != '.' && c != 'E' && c != 'e')
|
||||
{
|
||||
json_int_t intval;
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
|
||||
errno = 0;
|
||||
value = json_strtoint(saved_text, &end, 10);
|
||||
intval = json_strtoint(saved_text, &end, 10);
|
||||
if(errno == ERANGE) {
|
||||
if(value < 0)
|
||||
error_set(error, lex, "too big negative integer");
|
||||
if(intval < 0)
|
||||
error_set(error, lex, json_error_numeric_overflow, "too big negative integer");
|
||||
else
|
||||
error_set(error, lex, "too big integer");
|
||||
error_set(error, lex, json_error_numeric_overflow, "too big integer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
lex->token = TOKEN_INTEGER;
|
||||
lex->value.integer = value;
|
||||
lex->value.integer = intval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -509,9 +553,9 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
}
|
||||
lex_save(lex, c);
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c));
|
||||
}
|
||||
|
||||
if(c == 'E' || c == 'e') {
|
||||
@@ -524,20 +568,20 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c));
|
||||
}
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
if(jsonp_strtod(&lex->saved_text, &value)) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
if(jsonp_strtod(&lex->saved_text, &doubleval)) {
|
||||
error_set(error, lex, json_error_numeric_overflow, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lex->token = TOKEN_REAL;
|
||||
lex->value.real = value;
|
||||
lex->value.real = doubleval;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -550,14 +594,12 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
jsonp_free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
if(lex->token == TOKEN_STRING)
|
||||
lex_free_string(lex);
|
||||
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
do
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r');
|
||||
|
||||
if(c == STREAM_STATE_EOF) {
|
||||
lex->token = TOKEN_EOF;
|
||||
@@ -586,9 +628,9 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
/* eat up the whole identifier for clearer error messages */
|
||||
const char *saved_text;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isalpha(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isalpha(c));
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
@@ -614,23 +656,25 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
static char *lex_steal_string(lex_t *lex, size_t *out_len)
|
||||
{
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
result = lex->value.string.val;
|
||||
*out_len = lex->value.string.len;
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
static int lex_init(lex_t *lex, get_func get, size_t flags, void *data)
|
||||
{
|
||||
stream_init(&lex->stream, get, data);
|
||||
if(strbuffer_init(&lex->saved_text))
|
||||
return -1;
|
||||
|
||||
lex->flags = flags;
|
||||
lex->token = TOKEN_INVALID;
|
||||
return 0;
|
||||
}
|
||||
@@ -638,7 +682,7 @@ static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
static void lex_close(lex_t *lex)
|
||||
{
|
||||
if(lex->token == TOKEN_STRING)
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
strbuffer_close(&lex->saved_text);
|
||||
}
|
||||
|
||||
@@ -659,21 +703,27 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
while(1) {
|
||||
char *key;
|
||||
size_t len;
|
||||
json_t *value;
|
||||
|
||||
if(lex->token != TOKEN_STRING) {
|
||||
error_set(error, lex, "string or '}' expected");
|
||||
error_set(error, lex, json_error_invalid_syntax, "string or '}' expected");
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = lex_steal_string(lex);
|
||||
key = lex_steal_string(lex, &len);
|
||||
if(!key)
|
||||
return NULL;
|
||||
if (memchr(key, '\0', len)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, json_error_null_byte_in_key, "NUL byte in object key not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "duplicate object key");
|
||||
error_set(error, lex, json_error_duplicate_key, "duplicate object key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@@ -681,7 +731,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ':') {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "':' expected");
|
||||
error_set(error, lex, json_error_invalid_syntax, "':' expected");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -692,13 +742,11 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(json_object_set_nocheck(object, key, value)) {
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
jsonp_free(key);
|
||||
json_decref(value);
|
||||
goto error;
|
||||
}
|
||||
|
||||
json_decref(value);
|
||||
jsonp_free(key);
|
||||
|
||||
lex_scan(lex, error);
|
||||
@@ -709,7 +757,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
}
|
||||
|
||||
if(lex->token != '}') {
|
||||
error_set(error, lex, "'}' expected");
|
||||
error_set(error, lex, json_error_invalid_syntax, "'}' expected");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -735,11 +783,9 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
if(!elem)
|
||||
goto error;
|
||||
|
||||
if(json_array_append(array, elem)) {
|
||||
json_decref(elem);
|
||||
if(json_array_append_new(array, elem)) {
|
||||
goto error;
|
||||
}
|
||||
json_decref(elem);
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ',')
|
||||
@@ -749,7 +795,7 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
}
|
||||
|
||||
if(lex->token != ']') {
|
||||
error_set(error, lex, "']' expected");
|
||||
error_set(error, lex, json_error_invalid_syntax, "']' expected");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -764,9 +810,29 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
|
||||
lex->depth++;
|
||||
if(lex->depth > JSON_PARSER_MAX_DEPTH) {
|
||||
error_set(error, lex, json_error_stack_overflow, "maximum parsing depth reached");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(lex->token) {
|
||||
case TOKEN_STRING: {
|
||||
json = json_string_nocheck(lex->value.string);
|
||||
const char *value = lex->value.string.val;
|
||||
size_t len = lex->value.string.len;
|
||||
|
||||
if(!(flags & JSON_ALLOW_NUL)) {
|
||||
if(memchr(value, '\0', len)) {
|
||||
error_set(error, lex, json_error_null_character, "\\u0000 is not allowed without JSON_ALLOW_NUL");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
json = jsonp_stringn_nocheck_own(value, len);
|
||||
if(json) {
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -801,17 +867,18 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
break;
|
||||
|
||||
case TOKEN_INVALID:
|
||||
error_set(error, lex, "invalid token");
|
||||
error_set(error, lex, json_error_invalid_syntax, "invalid token");
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
error_set(error, lex, "unexpected token");
|
||||
error_set(error, lex, json_error_invalid_syntax, "unexpected token");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
lex->depth--;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -819,10 +886,12 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
lex->depth = 0;
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(!(flags & JSON_DECODE_ANY)) {
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
error_set(error, lex, "'[' or '{' expected");
|
||||
error_set(error, lex, json_error_invalid_syntax, "'[' or '{' expected");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -834,7 +903,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != TOKEN_EOF) {
|
||||
error_set(error, lex, "end of file expected");
|
||||
error_set(error, lex, json_error_end_of_input_expected, "end of file expected");
|
||||
json_decref(result);
|
||||
return NULL;
|
||||
}
|
||||
@@ -842,7 +911,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
if(error) {
|
||||
/* Save the position even though there was no error */
|
||||
error->position = lex->stream.position;
|
||||
error->position = (int)lex->stream.position;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -851,7 +920,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
typedef struct
|
||||
{
|
||||
const char *data;
|
||||
int pos;
|
||||
size_t pos;
|
||||
} string_data_t;
|
||||
|
||||
static int string_get(void *data)
|
||||
@@ -877,14 +946,14 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
jsonp_error_init(error, "<string>");
|
||||
|
||||
if (string == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
|
||||
if(lex_init(&lex, string_get, (void *)&stream_data))
|
||||
if(lex_init(&lex, string_get, flags, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
@@ -921,7 +990,7 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
||||
jsonp_error_init(error, "<buffer>");
|
||||
|
||||
if (buffer == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -929,7 +998,7 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
||||
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
||||
if(lex_init(&lex, buffer_get, flags, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
@@ -952,11 +1021,50 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
jsonp_error_init(error, source);
|
||||
|
||||
if (input == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
if(lex_init(&lex, (get_func)fgetc, flags, input))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int fd_get_func(int *fd)
|
||||
{
|
||||
uint8_t c;
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if (read(*fd, &c, 1) == 1)
|
||||
return c;
|
||||
#endif
|
||||
return EOF;
|
||||
}
|
||||
|
||||
json_t *json_loadfd(int input, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if(input == STDIN_FILENO)
|
||||
source = "<stdin>";
|
||||
else
|
||||
#endif
|
||||
source = "<stream>";
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
|
||||
if (input < 0) {
|
||||
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)fd_get_func, flags, &input))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
@@ -973,14 +1081,14 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
jsonp_error_init(error, path);
|
||||
|
||||
if (path == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if(!fp)
|
||||
{
|
||||
error_set(error, NULL, "unable to open %s: %s",
|
||||
error_set(error, NULL, json_error_cannot_open_file, "unable to open %s: %s",
|
||||
path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
@@ -990,3 +1098,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define MAX_BUF_LEN 1024
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char data[MAX_BUF_LEN];
|
||||
size_t len;
|
||||
size_t pos;
|
||||
json_load_callback_t callback;
|
||||
void *arg;
|
||||
} callback_data_t;
|
||||
|
||||
static int callback_get(void *data)
|
||||
{
|
||||
char c;
|
||||
callback_data_t *stream = data;
|
||||
|
||||
if(stream->pos >= stream->len) {
|
||||
stream->pos = 0;
|
||||
stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg);
|
||||
if(stream->len == 0 || stream->len == (size_t)-1)
|
||||
return EOF;
|
||||
}
|
||||
|
||||
c = stream->data[stream->pos];
|
||||
stream->pos++;
|
||||
return (unsigned char)c;
|
||||
}
|
||||
|
||||
json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
|
||||
callback_data_t stream_data;
|
||||
|
||||
memset(&stream_data, 0, sizeof(stream_data));
|
||||
stream_data.callback = callback;
|
||||
stream_data.arg = arg;
|
||||
|
||||
jsonp_error_init(error, "<callback>");
|
||||
|
||||
if (callback == NULL) {
|
||||
error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)callback_get, flags, &stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
381
src/lookup3.h
Normal file
381
src/lookup3.h
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
|
||||
These are functions for producing 32-bit hashes for hash table lookup.
|
||||
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
are externally useful functions. Routines to test the hash are included
|
||||
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
the public domain. It has no warranty.
|
||||
|
||||
You probably want to use hashlittle(). hashlittle() and hashbig()
|
||||
hash byte arrays. hashlittle() is is faster than hashbig() on
|
||||
little-endian machines. Intel and AMD are little-endian machines.
|
||||
On second thought, you probably want hashlittle2(), which is identical to
|
||||
hashlittle() except it returns two 32-bit hashes for the price of one.
|
||||
You could implement hashbig2() if you wanted but I haven't bothered here.
|
||||
|
||||
If you want to find a hash of, say, exactly 7 integers, do
|
||||
a = i1; b = i2; c = i3;
|
||||
mix(a,b,c);
|
||||
a += i4; b += i5; c += i6;
|
||||
mix(a,b,c);
|
||||
a += i7;
|
||||
final(a,b,c);
|
||||
then use c as the hash value. If you have a variable length array of
|
||||
4-byte integers to hash, use hashword(). If you have a byte array (like
|
||||
a character string), use hashlittle(). If you have several byte arrays, or
|
||||
a mix of things, see the comments above hashlittle().
|
||||
|
||||
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||
then mix those integers. This is fast (you can do a lot more thorough
|
||||
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h> /* defines uint32_t etc */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h> /* attempt to define endianness */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
# include <endian.h> /* attempt to define endianness */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* My best guess at if you are big-endian or little-endian. This may
|
||||
* need adjustment.
|
||||
*/
|
||||
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
|
||||
__BYTE_ORDER == __LITTLE_ENDIAN) || \
|
||||
(defined(i386) || defined(__i386__) || defined(__i486__) || \
|
||||
defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
|
||||
# define HASH_LITTLE_ENDIAN 1
|
||||
# define HASH_BIG_ENDIAN 0
|
||||
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
|
||||
__BYTE_ORDER == __BIG_ENDIAN) || \
|
||||
(defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
|
||||
# define HASH_LITTLE_ENDIAN 0
|
||||
# define HASH_BIG_ENDIAN 1
|
||||
#else
|
||||
# define HASH_LITTLE_ENDIAN 0
|
||||
# define HASH_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#define hashsize(n) ((uint32_t)1<<(n))
|
||||
#define hashmask(n) (hashsize(n)-1)
|
||||
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
|
||||
This is reversible, so any information in (a,b,c) before mix() is
|
||||
still in (a,b,c) after mix().
|
||||
|
||||
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||
mix() in reverse, there are at least 32 bits of the output that
|
||||
are sometimes the same for one pair and different for another pair.
|
||||
This was tested for:
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||
satisfy this are
|
||||
4 6 8 16 19 4
|
||||
9 15 3 18 27 15
|
||||
14 9 3 7 17 3
|
||||
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
the operations, constants, and arrangements of the variables.
|
||||
|
||||
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
avalanche in c.
|
||||
|
||||
This allows some parallelism. Read-after-writes are good at doubling
|
||||
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
direction as the goal of parallelism. I did what I could. Rotates
|
||||
seem to cost as much as shifts on every machine I could lay my hands
|
||||
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
rotates.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= rot(c, 4); c += b; \
|
||||
b -= a; b ^= rot(a, 6); a += c; \
|
||||
c -= b; c ^= rot(b, 8); b += a; \
|
||||
a -= c; a ^= rot(c,16); c += b; \
|
||||
b -= a; b ^= rot(a,19); a += c; \
|
||||
c -= b; c ^= rot(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
|
||||
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
produce values of c that look totally different. This was tested for
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
These constants passed:
|
||||
14 11 25 16 4 14 24
|
||||
12 14 25 16 4 14 24
|
||||
and these came close:
|
||||
4 8 15 26 3 22 24
|
||||
10 8 15 26 3 22 24
|
||||
11 8 15 26 3 22 24
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define final(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= rot(b,14); \
|
||||
a ^= c; a -= rot(c,11); \
|
||||
b ^= a; b -= rot(a,25); \
|
||||
c ^= b; c -= rot(b,16); \
|
||||
a ^= c; a -= rot(c,4); \
|
||||
b ^= a; b -= rot(a,14); \
|
||||
c ^= b; c -= rot(b,24); \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
hashlittle() -- hash a variable-length key into a 32-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
length : the length of the key, counting by bytes
|
||||
initval : can be any 4-byte value
|
||||
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
the return value. Two keys differing by one or two bits will have
|
||||
totally different hash values.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (uint8_t **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
|
||||
/* Detect Valgrind or AddressSanitizer */
|
||||
#ifdef VALGRIND
|
||||
# define NO_MASKING_TRICK 1
|
||||
#else
|
||||
# if defined(__has_feature) /* Clang */
|
||||
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# else
|
||||
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NO_MASKING_TRICK
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* 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).
|
||||
*/
|
||||
#ifndef NO_MASKING_TRICK
|
||||
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
|
||||
case 5 : b+=k[1]&0xff; a+=k[0]; break;
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=k[0]&0xffffff; break;
|
||||
case 2 : a+=k[0]&0xffff; break;
|
||||
case 1 : a+=k[0]&0xff; break;
|
||||
case 0 : return c; /* zero length strings require no mixing */
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return c;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*--------------- all but last block: aligned reads and different mixing */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=k[4];
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=k[2];
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=k[0];
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return c; /* zero length requires no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return c;
|
||||
}
|
||||
26
src/memory.c
26
src/memory.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify it
|
||||
@@ -9,9 +9,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
|
||||
/* C89 allows these to be macros */
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
/* memory function pointers */
|
||||
static json_malloc_t do_malloc = malloc;
|
||||
static json_free_t do_free = free;
|
||||
@@ -33,14 +37,20 @@ void jsonp_free(void *ptr)
|
||||
}
|
||||
|
||||
char *jsonp_strdup(const char *str)
|
||||
{
|
||||
return jsonp_strndup(str, strlen(str));
|
||||
}
|
||||
|
||||
char *jsonp_strndup(const char *str, size_t len)
|
||||
{
|
||||
char *new_str;
|
||||
|
||||
new_str = jsonp_malloc(strlen(str) + 1);
|
||||
new_str = jsonp_malloc(len + 1);
|
||||
if(!new_str)
|
||||
return NULL;
|
||||
|
||||
strcpy(new_str, str);
|
||||
memcpy(new_str, str, len);
|
||||
new_str[len] = '\0';
|
||||
return new_str;
|
||||
}
|
||||
|
||||
@@ -49,3 +59,11 @@ void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
|
||||
do_malloc = malloc_fn;
|
||||
do_free = free_fn;
|
||||
}
|
||||
|
||||
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
|
||||
{
|
||||
if (malloc_fn)
|
||||
*malloc_fn = do_malloc;
|
||||
if (free_fn)
|
||||
*free_fn = do_free;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
@@ -7,21 +7,34 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
#include "utf.h"
|
||||
|
||||
typedef struct {
|
||||
int line;
|
||||
int column;
|
||||
size_t pos;
|
||||
char token;
|
||||
} token_t;
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
const char *fmt;
|
||||
char token;
|
||||
token_t prev_token;
|
||||
token_t token;
|
||||
token_t next_token;
|
||||
json_error_t *error;
|
||||
size_t flags;
|
||||
int line;
|
||||
int column;
|
||||
size_t pos;
|
||||
int has_error;
|
||||
} scanner_t;
|
||||
|
||||
static const char *type_names[] = {
|
||||
#define token(scanner) ((scanner)->token.token)
|
||||
|
||||
static const char * const type_names[] = {
|
||||
"object",
|
||||
"array",
|
||||
"string",
|
||||
@@ -34,8 +47,7 @@ static const char *type_names[] = {
|
||||
|
||||
#define type_name(x) type_names[json_typeof(x)]
|
||||
|
||||
static const char *unpack_value_starters = "{[siIbfFOon";
|
||||
|
||||
static const char unpack_value_starters[] = "{[siIbfFOon";
|
||||
|
||||
static void scanner_init(scanner_t *s, json_error_t *error,
|
||||
size_t flags, const char *fmt)
|
||||
@@ -43,14 +55,29 @@ static void scanner_init(scanner_t *s, json_error_t *error,
|
||||
s->error = error;
|
||||
s->flags = flags;
|
||||
s->fmt = s->start = fmt;
|
||||
memset(&s->prev_token, 0, sizeof(token_t));
|
||||
memset(&s->token, 0, sizeof(token_t));
|
||||
memset(&s->next_token, 0, sizeof(token_t));
|
||||
s->line = 1;
|
||||
s->column = 0;
|
||||
s->pos = 0;
|
||||
s->has_error = 0;
|
||||
}
|
||||
|
||||
static void next_token(scanner_t *s)
|
||||
{
|
||||
const char *t = s->fmt;
|
||||
const char *t;
|
||||
s->prev_token = s->token;
|
||||
|
||||
if(s->next_token.line) {
|
||||
s->token = s->next_token;
|
||||
s->next_token.line = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
t = s->fmt;
|
||||
s->column++;
|
||||
s->pos++;
|
||||
|
||||
/* skip space and ignored chars */
|
||||
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
|
||||
@@ -61,23 +88,33 @@ static void next_token(scanner_t *s)
|
||||
else
|
||||
s->column++;
|
||||
|
||||
s->pos++;
|
||||
t++;
|
||||
}
|
||||
|
||||
s->token = *t;
|
||||
s->token.token = *t;
|
||||
s->token.line = s->line;
|
||||
s->token.column = s->column;
|
||||
s->token.pos = s->pos;
|
||||
|
||||
t++;
|
||||
s->fmt = t;
|
||||
}
|
||||
|
||||
static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
|
||||
static void prev_token(scanner_t *s)
|
||||
{
|
||||
s->next_token = s->token;
|
||||
s->token = s->prev_token;
|
||||
}
|
||||
|
||||
static void set_error(scanner_t *s, const char *source, enum json_error_code code,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
size_t pos;
|
||||
va_start(ap, fmt);
|
||||
|
||||
pos = (size_t)(s->fmt - s->start);
|
||||
jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
|
||||
jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
|
||||
code, fmt, ap);
|
||||
|
||||
jsonp_error_set_source(s->error, source);
|
||||
|
||||
@@ -86,51 +123,153 @@ static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
|
||||
|
||||
static json_t *pack(scanner_t *s, va_list *ap);
|
||||
|
||||
|
||||
/* ours will be set to 1 if jsonp_free() must be called for the result
|
||||
afterwards */
|
||||
static char *read_string(scanner_t *s, va_list *ap,
|
||||
const char *purpose, size_t *out_len, int *ours)
|
||||
{
|
||||
char t;
|
||||
strbuffer_t strbuff;
|
||||
const char *str;
|
||||
size_t length;
|
||||
|
||||
next_token(s);
|
||||
t = token(s);
|
||||
prev_token(s);
|
||||
|
||||
*ours = 0;
|
||||
if(t != '#' && t != '%' && t != '+') {
|
||||
/* Optimize the simple case */
|
||||
str = va_arg(*ap, const char *);
|
||||
|
||||
if(!str) {
|
||||
set_error(s, "<args>", json_error_null_value, "NULL string argument");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
length = strlen(str);
|
||||
|
||||
if(!utf8_check_string(str, length)) {
|
||||
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = length;
|
||||
return (char *)str;
|
||||
}
|
||||
|
||||
strbuffer_init(&strbuff);
|
||||
|
||||
while(1) {
|
||||
str = va_arg(*ap, const char *);
|
||||
if(!str) {
|
||||
set_error(s, "<args>", json_error_null_value, "NULL string argument");
|
||||
s->has_error = 1;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '#') {
|
||||
length = va_arg(*ap, int);
|
||||
}
|
||||
else if(token(s) == '%') {
|
||||
length = va_arg(*ap, size_t);
|
||||
}
|
||||
else {
|
||||
prev_token(s);
|
||||
length = s->has_error ? 0 : strlen(str);
|
||||
}
|
||||
|
||||
if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) {
|
||||
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||
s->has_error = 1;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
if(token(s) != '+') {
|
||||
prev_token(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(s->has_error) {
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!utf8_check_string(strbuff.value, strbuff.length)) {
|
||||
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
|
||||
strbuffer_close(&strbuff);
|
||||
s->has_error = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = strbuff.length;
|
||||
*ours = 1;
|
||||
return strbuffer_steal_value(&strbuff);
|
||||
}
|
||||
|
||||
static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
{
|
||||
json_t *object = json_object();
|
||||
next_token(s);
|
||||
|
||||
while(s->token != '}') {
|
||||
const char *key;
|
||||
while(token(s) != '}') {
|
||||
char *key;
|
||||
size_t len;
|
||||
int ours;
|
||||
json_t *value;
|
||||
|
||||
if(!s->token) {
|
||||
set_error(s, "<format>", "Unexpected end of format string");
|
||||
if(!token(s)) {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(s->token != 's') {
|
||||
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
|
||||
if(token(s) != 's') {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = va_arg(*ap, const char *);
|
||||
if(!key) {
|
||||
set_error(s, "<args>", "NULL object key");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(!utf8_check_string(key, -1)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 in object key");
|
||||
goto error;
|
||||
}
|
||||
key = read_string(s, ap, "object key", &len, &ours);
|
||||
if (!key)
|
||||
s->has_error = 1;
|
||||
|
||||
next_token(s);
|
||||
|
||||
value = pack(s, ap);
|
||||
if(!value)
|
||||
goto error;
|
||||
if(!value) {
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
||||
goto error;
|
||||
if(strchr("soO", token(s)) && s->next_token.token == '*') {
|
||||
next_token(s);
|
||||
} else {
|
||||
s->has_error = 1;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(s->has_error)
|
||||
json_decref(value);
|
||||
|
||||
if(!s->has_error && json_object_set_new_nocheck(object, key, value)) {
|
||||
set_error(s, "<internal>", json_error_out_of_memory, "Unable to add key \"%s\"", key);
|
||||
s->has_error = 1;
|
||||
}
|
||||
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
if(strchr("soO", token(s)) && s->next_token.token == '*')
|
||||
next_token(s);
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
return object;
|
||||
if(!s->has_error)
|
||||
return object;
|
||||
|
||||
error:
|
||||
json_decref(object);
|
||||
@@ -142,35 +281,73 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
|
||||
json_t *array = json_array();
|
||||
next_token(s);
|
||||
|
||||
while(s->token != ']') {
|
||||
while(token(s) != ']') {
|
||||
json_t *value;
|
||||
|
||||
if(!s->token) {
|
||||
set_error(s, "<format>", "Unexpected end of format string");
|
||||
if(!token(s)) {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||
/* Format string errors are unrecoverable. */
|
||||
goto error;
|
||||
}
|
||||
|
||||
value = pack(s, ap);
|
||||
if(!value)
|
||||
goto error;
|
||||
if(!value) {
|
||||
if(strchr("soO", token(s)) && s->next_token.token == '*') {
|
||||
next_token(s);
|
||||
} else {
|
||||
s->has_error = 1;
|
||||
}
|
||||
|
||||
if(json_array_append_new(array, value)) {
|
||||
set_error(s, "<internal>", "Unable to append to array");
|
||||
goto error;
|
||||
next_token(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(s->has_error)
|
||||
json_decref(value);
|
||||
|
||||
if(!s->has_error && json_array_append_new(array, value)) {
|
||||
set_error(s, "<internal>", json_error_out_of_memory, "Unable to append to array");
|
||||
s->has_error = 1;
|
||||
}
|
||||
|
||||
if(strchr("soO", token(s)) && s->next_token.token == '*')
|
||||
next_token(s);
|
||||
next_token(s);
|
||||
}
|
||||
return array;
|
||||
|
||||
if(!s->has_error)
|
||||
return array;
|
||||
|
||||
error:
|
||||
json_decref(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *pack_string(scanner_t *s, va_list *ap)
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int ours;
|
||||
int nullable;
|
||||
|
||||
next_token(s);
|
||||
nullable = token(s) == '?';
|
||||
if (!nullable)
|
||||
prev_token(s);
|
||||
|
||||
str = read_string(s, ap, "string", &len, &ours);
|
||||
if (!str) {
|
||||
return nullable ? json_null() : NULL;
|
||||
} else if (ours) {
|
||||
return jsonp_stringn_nocheck_own(str, len);
|
||||
} else {
|
||||
return json_stringn_nocheck(str, len);
|
||||
}
|
||||
}
|
||||
|
||||
static json_t *pack(scanner_t *s, va_list *ap)
|
||||
{
|
||||
switch(s->token) {
|
||||
switch(token(s)) {
|
||||
case '{':
|
||||
return pack_object(s, ap);
|
||||
|
||||
@@ -178,18 +355,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
return pack_array(s, ap);
|
||||
|
||||
case 's': /* string */
|
||||
{
|
||||
const char *str = va_arg(*ap, const char *);
|
||||
if(!str) {
|
||||
set_error(s, "<args>", "NULL string argument");
|
||||
return NULL;
|
||||
}
|
||||
if(!utf8_check_string(str, -1)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 string");
|
||||
return NULL;
|
||||
}
|
||||
return json_string_nocheck(str);
|
||||
}
|
||||
return pack_string(s, ap);
|
||||
|
||||
case 'n': /* null */
|
||||
return json_null();
|
||||
@@ -207,14 +373,45 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
return json_real(va_arg(*ap, double));
|
||||
|
||||
case 'O': /* a json_t object; increments refcount */
|
||||
return json_incref(va_arg(*ap, json_t *));
|
||||
{
|
||||
int nullable;
|
||||
json_t *json;
|
||||
|
||||
next_token(s);
|
||||
nullable = token(s) == '?';
|
||||
if (!nullable)
|
||||
prev_token(s);
|
||||
|
||||
json = va_arg(*ap, json_t *);
|
||||
if (!json && nullable) {
|
||||
return json_null();
|
||||
} else {
|
||||
return json_incref(json);
|
||||
}
|
||||
}
|
||||
|
||||
case 'o': /* a json_t object; doesn't increment refcount */
|
||||
return va_arg(*ap, json_t *);
|
||||
{
|
||||
int nullable;
|
||||
json_t *json;
|
||||
|
||||
next_token(s);
|
||||
nullable = token(s) == '?';
|
||||
if (!nullable)
|
||||
prev_token(s);
|
||||
|
||||
json = va_arg(*ap, json_t *);
|
||||
if (!json && nullable) {
|
||||
return json_null();
|
||||
} else {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
s->token);
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
|
||||
token(s));
|
||||
s->has_error = 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -225,6 +422,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
{
|
||||
int ret = -1;
|
||||
int strict = 0;
|
||||
int gotopt = 0;
|
||||
|
||||
/* Use a set (emulated by a hashtable) to check that all object
|
||||
keys are accessed. Checking that the correct number of keys
|
||||
@@ -234,54 +432,54 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
hashtable_t key_set;
|
||||
|
||||
if(hashtable_init(&key_set)) {
|
||||
set_error(s, "<internal>", "Out of memory");
|
||||
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(root && !json_is_object(root)) {
|
||||
set_error(s, "<validation>", "Expected object, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s",
|
||||
type_name(root));
|
||||
goto out;
|
||||
}
|
||||
next_token(s);
|
||||
|
||||
while(s->token != '}') {
|
||||
while(token(s) != '}') {
|
||||
const char *key;
|
||||
json_t *value;
|
||||
int opt = 0;
|
||||
|
||||
if(strict != 0) {
|
||||
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
|
||||
(strict == 1 ? '!' : '*'), s->token);
|
||||
set_error(s, "<format>", json_error_invalid_format, "Expected '}' after '%c', got '%c'",
|
||||
(strict == 1 ? '!' : '*'), token(s));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!s->token) {
|
||||
set_error(s, "<format>", "Unexpected end of format string");
|
||||
if(!token(s)) {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(s->token == '!' || s->token == '*') {
|
||||
strict = (s->token == '!' ? 1 : -1);
|
||||
if(token(s) == '!' || token(s) == '*') {
|
||||
strict = (token(s) == '!' ? 1 : -1);
|
||||
next_token(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(s->token != 's') {
|
||||
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
|
||||
if(token(s) != 's') {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
|
||||
goto out;
|
||||
}
|
||||
|
||||
key = va_arg(*ap, const char *);
|
||||
if(!key) {
|
||||
set_error(s, "<args>", "NULL object key");
|
||||
set_error(s, "<args>", json_error_null_value, "NULL object key");
|
||||
goto out;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
|
||||
if(s->token == '?') {
|
||||
opt = 1;
|
||||
if(token(s) == '?') {
|
||||
opt = gotopt = 1;
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
@@ -292,7 +490,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
else {
|
||||
value = json_object_get(root, key);
|
||||
if(!value && !opt) {
|
||||
set_error(s, "<validation>", "Object item not found: %s", key);
|
||||
set_error(s, "<validation>", json_error_item_not_found, "Object item not found: %s", key);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -300,17 +498,61 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
if(unpack(s, value, ap))
|
||||
goto out;
|
||||
|
||||
hashtable_set(&key_set, key, 0, json_null());
|
||||
hashtable_set(&key_set, key, json_null());
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||
strict = 1;
|
||||
|
||||
if(root && strict == 1 && key_set.size != json_object_size(root)) {
|
||||
long diff = (long)json_object_size(root) - (long)key_set.size;
|
||||
set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
|
||||
goto out;
|
||||
if(root && strict == 1) {
|
||||
/* We need to check that all non optional items have been parsed */
|
||||
const char *key;
|
||||
int have_unrecognized_keys = 0;
|
||||
strbuffer_t unrecognized_keys;
|
||||
json_t *value;
|
||||
long unpacked = 0;
|
||||
if (gotopt) {
|
||||
/* We have optional keys, we need to iter on each key */
|
||||
json_object_foreach(root, key, value) {
|
||||
if(!hashtable_get(&key_set, key)) {
|
||||
unpacked++;
|
||||
|
||||
/* Save unrecognized keys for the error message */
|
||||
if (!have_unrecognized_keys) {
|
||||
strbuffer_init(&unrecognized_keys);
|
||||
have_unrecognized_keys = 1;
|
||||
} else {
|
||||
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
|
||||
}
|
||||
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* No optional keys, we can just compare the number of items */
|
||||
unpacked = (long)json_object_size(root) - (long)key_set.size;
|
||||
}
|
||||
if (unpacked) {
|
||||
if (!gotopt) {
|
||||
/* Save unrecognized keys for the error message */
|
||||
json_object_foreach(root, key, value) {
|
||||
if(!hashtable_get(&key_set, key)) {
|
||||
if (!have_unrecognized_keys) {
|
||||
strbuffer_init(&unrecognized_keys);
|
||||
have_unrecognized_keys = 1;
|
||||
} else {
|
||||
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
|
||||
}
|
||||
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
set_error(s, "<validation>", json_error_end_of_input_expected,
|
||||
"%li object item(s) left unpacked: %s",
|
||||
unpacked, strbuffer_value(&unrecognized_keys));
|
||||
strbuffer_close(&unrecognized_keys);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@@ -326,35 +568,35 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||
int strict = 0;
|
||||
|
||||
if(root && !json_is_array(root)) {
|
||||
set_error(s, "<validation>", "Expected array, got %s", type_name(root));
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s", type_name(root));
|
||||
return -1;
|
||||
}
|
||||
next_token(s);
|
||||
|
||||
while(s->token != ']') {
|
||||
while(token(s) != ']') {
|
||||
json_t *value;
|
||||
|
||||
if(strict != 0) {
|
||||
set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
|
||||
set_error(s, "<format>", json_error_invalid_format, "Expected ']' after '%c', got '%c'",
|
||||
(strict == 1 ? '!' : '*'),
|
||||
s->token);
|
||||
token(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!s->token) {
|
||||
set_error(s, "<format>", "Unexpected end of format string");
|
||||
if(!token(s)) {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(s->token == '!' || s->token == '*') {
|
||||
strict = (s->token == '!' ? 1 : -1);
|
||||
if(token(s) == '!' || token(s) == '*') {
|
||||
strict = (token(s) == '!' ? 1 : -1);
|
||||
next_token(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!strchr(unpack_value_starters, s->token)) {
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
s->token);
|
||||
if(!strchr(unpack_value_starters, token(s))) {
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
|
||||
token(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -365,7 +607,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||
else {
|
||||
value = json_array_get(root, i);
|
||||
if(!value) {
|
||||
set_error(s, "<validation>", "Array index %lu out of range",
|
||||
set_error(s, "<validation>", json_error_index_out_of_range, "Array index %lu out of range",
|
||||
(unsigned long)i);
|
||||
return -1;
|
||||
}
|
||||
@@ -383,7 +625,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
if(root && strict == 1 && i != json_array_size(root)) {
|
||||
long diff = (long)json_array_size(root) - (long)i;
|
||||
set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
|
||||
set_error(s, "<validation>", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -392,7 +634,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
{
|
||||
switch(s->token)
|
||||
switch(token(s))
|
||||
{
|
||||
case '{':
|
||||
return unpack_object(s, root, ap);
|
||||
@@ -402,28 +644,44 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
case 's':
|
||||
if(root && !json_is_string(root)) {
|
||||
set_error(s, "<validation>", "Expected string, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected string, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
const char **target;
|
||||
const char **str_target;
|
||||
size_t *len_target = NULL;
|
||||
|
||||
target = va_arg(*ap, const char **);
|
||||
if(!target) {
|
||||
set_error(s, "<args>", "NULL string argument");
|
||||
str_target = va_arg(*ap, const char **);
|
||||
if(!str_target) {
|
||||
set_error(s, "<args>", json_error_null_value, "NULL string argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(root)
|
||||
*target = json_string_value(root);
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '%') {
|
||||
len_target = va_arg(*ap, size_t *);
|
||||
if(!len_target) {
|
||||
set_error(s, "<args>", json_error_null_value, "NULL string length argument");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
prev_token(s);
|
||||
|
||||
if(root) {
|
||||
*str_target = json_string_value(root);
|
||||
if(len_target)
|
||||
*len_target = json_string_length(root);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 'i':
|
||||
if(root && !json_is_integer(root)) {
|
||||
set_error(s, "<validation>", "Expected integer, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
@@ -431,14 +689,14 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
int *target = va_arg(*ap, int*);
|
||||
if(root)
|
||||
*target = json_integer_value(root);
|
||||
*target = (int)json_integer_value(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'I':
|
||||
if(root && !json_is_integer(root)) {
|
||||
set_error(s, "<validation>", "Expected integer, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
@@ -453,7 +711,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
case 'b':
|
||||
if(root && !json_is_boolean(root)) {
|
||||
set_error(s, "<validation>", "Expected true or false, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected true or false, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
@@ -468,7 +726,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
case 'f':
|
||||
if(root && !json_is_real(root)) {
|
||||
set_error(s, "<validation>", "Expected real, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected real, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
@@ -483,7 +741,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
case 'F':
|
||||
if(root && !json_is_number(root)) {
|
||||
set_error(s, "<validation>", "Expected real or integer, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected real or integer, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
@@ -513,15 +771,15 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
case 'n':
|
||||
/* Never assign, just validate */
|
||||
if(root && !json_is_null(root)) {
|
||||
set_error(s, "<validation>", "Expected null, got %s",
|
||||
set_error(s, "<validation>", json_error_wrong_type, "Expected null, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
s->token);
|
||||
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
|
||||
token(s));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -535,7 +793,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
|
||||
|
||||
if(!fmt || !*fmt) {
|
||||
jsonp_error_init(error, "<format>");
|
||||
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
|
||||
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
|
||||
return NULL;
|
||||
}
|
||||
jsonp_error_init(error, NULL);
|
||||
@@ -551,9 +809,13 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
|
||||
return NULL;
|
||||
|
||||
next_token(&s);
|
||||
if(s.token) {
|
||||
if(token(&s)) {
|
||||
json_decref(value);
|
||||
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
|
||||
return NULL;
|
||||
}
|
||||
if(s.has_error) {
|
||||
json_decref(value);
|
||||
set_error(&s, "<format>", "Garbage after format string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -592,13 +854,13 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
|
||||
|
||||
if(!root) {
|
||||
jsonp_error_init(error, "<root>");
|
||||
jsonp_error_set(error, -1, -1, 0, "NULL root value");
|
||||
jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!fmt || !*fmt) {
|
||||
jsonp_error_init(error, "<format>");
|
||||
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
|
||||
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
|
||||
return -1;
|
||||
}
|
||||
jsonp_error_init(error, NULL);
|
||||
@@ -614,8 +876,8 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
|
||||
va_end(ap_copy);
|
||||
|
||||
next_token(&s);
|
||||
if(s.token) {
|
||||
set_error(&s, "<format>", "Garbage after format string");
|
||||
if(token(&s)) {
|
||||
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "jansson_private.h"
|
||||
@@ -31,7 +34,9 @@ int strbuffer_init(strbuffer_t *strbuff)
|
||||
|
||||
void strbuffer_close(strbuffer_t *strbuff)
|
||||
{
|
||||
jsonp_free(strbuff->value);
|
||||
if(strbuff->value)
|
||||
jsonp_free(strbuff->value);
|
||||
|
||||
strbuff->size = 0;
|
||||
strbuff->length = 0;
|
||||
strbuff->value = NULL;
|
||||
@@ -51,15 +56,10 @@ const char *strbuffer_value(const strbuffer_t *strbuff)
|
||||
char *strbuffer_steal_value(strbuffer_t *strbuff)
|
||||
{
|
||||
char *result = strbuff->value;
|
||||
strbuffer_init(strbuff);
|
||||
strbuff->value = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
int strbuffer_append(strbuffer_t *strbuff, const char *string)
|
||||
{
|
||||
return strbuffer_append_bytes(strbuff, string, strlen(string));
|
||||
}
|
||||
|
||||
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
|
||||
{
|
||||
return strbuffer_append_bytes(strbuff, &byte, 1);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,6 +8,8 @@
|
||||
#ifndef STRBUFFER_H
|
||||
#define STRBUFFER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
char *value;
|
||||
size_t length; /* bytes used */
|
||||
@@ -20,9 +22,10 @@ void strbuffer_close(strbuffer_t *strbuff);
|
||||
void strbuffer_clear(strbuffer_t *strbuff);
|
||||
|
||||
const char *strbuffer_value(const strbuffer_t *strbuff);
|
||||
|
||||
/* Steal the value and close the strbuffer */
|
||||
char *strbuffer_steal_value(strbuffer_t *strbuff);
|
||||
|
||||
int strbuffer_append(strbuffer_t *strbuff, const char *string);
|
||||
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
|
||||
|
||||
|
||||
@@ -2,9 +2,22 @@
|
||||
#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"
|
||||
|
||||
/* 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>
|
||||
|
||||
@@ -64,7 +77,7 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
value = strtod(strbuffer->value, &end);
|
||||
assert(end == strbuffer->value + strbuffer->length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
|
||||
/* Overflow */
|
||||
return -1;
|
||||
}
|
||||
@@ -73,13 +86,16 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value)
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
|
||||
{
|
||||
int ret;
|
||||
char *start, *end;
|
||||
size_t length;
|
||||
|
||||
ret = snprintf(buffer, size, "%.17g", value);
|
||||
if (precision == 0)
|
||||
precision = 17;
|
||||
|
||||
ret = snprintf(buffer, size, "%.*g", precision, value);
|
||||
if(ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
29
src/utf.c
29
src/utf.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <string.h>
|
||||
#include "utf.h"
|
||||
|
||||
int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
|
||||
{
|
||||
if(codepoint < 0)
|
||||
return -1;
|
||||
@@ -44,7 +44,7 @@ int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int utf8_check_first(char byte)
|
||||
size_t utf8_check_first(char byte)
|
||||
{
|
||||
unsigned char u = (unsigned char)byte;
|
||||
|
||||
@@ -80,9 +80,9 @@ int utf8_check_first(char byte)
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
int32_t value = 0;
|
||||
unsigned char u = (unsigned char)buffer[0];
|
||||
|
||||
@@ -136,12 +136,12 @@ int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
|
||||
{
|
||||
int count;
|
||||
size_t count;
|
||||
int32_t value;
|
||||
|
||||
if(!*buffer)
|
||||
if(!bufsize)
|
||||
return buffer;
|
||||
|
||||
count = utf8_check_first(buffer[0]);
|
||||
@@ -152,7 +152,7 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
value = (unsigned char)buffer[0];
|
||||
else
|
||||
{
|
||||
if(!utf8_check_full(buffer, count, &value))
|
||||
if(count > bufsize || !utf8_check_full(buffer, count, &value))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -162,21 +162,18 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
return buffer + count;
|
||||
}
|
||||
|
||||
int utf8_check_string(const char *string, int length)
|
||||
int utf8_check_string(const char *string, size_t length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(length == -1)
|
||||
length = strlen(string);
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < length; i++)
|
||||
{
|
||||
int count = utf8_check_first(string[i]);
|
||||
size_t count = utf8_check_first(string[i]);
|
||||
if(count == 0)
|
||||
return 0;
|
||||
else if(count > 1)
|
||||
{
|
||||
if(i + count > length)
|
||||
if(count > length - i)
|
||||
return 0;
|
||||
|
||||
if(!utf8_check_full(&string[i], count, NULL))
|
||||
|
||||
34
src/utf.h
34
src/utf.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,31 +9,19 @@
|
||||
#define UTF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
/* inttypes.h includes stdint.h in a standard environment, so there's
|
||||
no need to include stdint.h separately. If inttypes.h doesn't define
|
||||
int32_t, it's defined in config.h. */
|
||||
#include <inttypes.h>
|
||||
#endif /* HAVE_INTTYPES_H */
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#else /* !HAVE_CONFIG_H */
|
||||
#ifdef _WIN32
|
||||
typedef int int32_t;
|
||||
#else /* !_WIN32 */
|
||||
/* Assume a standard environment */
|
||||
#include <inttypes.h>
|
||||
#endif /* _WIN32 */
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
|
||||
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
size_t utf8_check_first(char byte);
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
|
||||
int utf8_check_first(char byte);
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
|
||||
|
||||
int utf8_check_string(const char *string, int length);
|
||||
int utf8_check_string(const char *string, size_t length);
|
||||
|
||||
#endif
|
||||
|
||||
390
src/value.c
390
src/value.c
@@ -1,21 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
#include "jansson_private.h"
|
||||
#include "utf.h"
|
||||
|
||||
/* Work around nonstandard isnan() and isinf() implementations */
|
||||
#ifndef isnan
|
||||
#ifndef __sun
|
||||
static JSON_INLINE int isnan(double x) { return x != x; }
|
||||
#endif
|
||||
#endif
|
||||
#ifndef isinf
|
||||
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
||||
#endif
|
||||
|
||||
static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||
{
|
||||
@@ -26,11 +46,19 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||
|
||||
/*** object ***/
|
||||
|
||||
extern volatile uint32_t hashtable_seed;
|
||||
|
||||
json_t *json_object(void)
|
||||
{
|
||||
json_object_t *object = jsonp_malloc(sizeof(json_object_t));
|
||||
if(!object)
|
||||
return NULL;
|
||||
|
||||
if (!hashtable_seed) {
|
||||
/* Autoseed */
|
||||
json_object_seed(0);
|
||||
}
|
||||
|
||||
json_init(&object->json, JSON_OBJECT);
|
||||
|
||||
if(hashtable_init(&object->hashtable))
|
||||
@@ -39,9 +67,6 @@ json_t *json_object(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
object->serial = 0;
|
||||
object->visited = 0;
|
||||
|
||||
return &object->json;
|
||||
}
|
||||
|
||||
@@ -66,7 +91,7 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !json_is_object(json))
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
@@ -87,7 +112,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
}
|
||||
object = json_to_object(json);
|
||||
|
||||
if(hashtable_set(&object->hashtable, key, object->serial++, value))
|
||||
if(hashtable_set(&object->hashtable, key, value))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@@ -98,7 +123,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!key || !utf8_check_string(key, -1))
|
||||
if(!key || !utf8_check_string(key, strlen(key)))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@@ -111,7 +136,7 @@ int json_object_del(json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
@@ -126,9 +151,7 @@ int json_object_clear(json_t *json)
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
|
||||
hashtable_clear(&object->hashtable);
|
||||
object->serial = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -233,7 +256,10 @@ json_t *json_object_iter_value(void *iter)
|
||||
int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
|
||||
{
|
||||
if(!json_is_object(json) || !iter || !value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hashtable_iter_set(iter, value);
|
||||
return 0;
|
||||
@@ -247,15 +273,15 @@ void *json_object_key_to_iter(const char *key)
|
||||
return hashtable_key_to_iter(key);
|
||||
}
|
||||
|
||||
static int json_object_equal(json_t *object1, json_t *object2)
|
||||
static int json_object_equal(const json_t *object1, const json_t *object2)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
const json_t *value1, *value2;
|
||||
|
||||
if(json_object_size(object1) != json_object_size(object2))
|
||||
return 0;
|
||||
|
||||
json_object_foreach(object1, key, value1) {
|
||||
json_object_foreach((json_t *)object1, key, value1) {
|
||||
value2 = json_object_get(object2, key);
|
||||
|
||||
if(!json_equal(value1, value2))
|
||||
@@ -282,19 +308,27 @@ static json_t *json_object_copy(json_t *object)
|
||||
return result;
|
||||
}
|
||||
|
||||
static json_t *json_object_deep_copy(json_t *object)
|
||||
static json_t *json_object_deep_copy(const json_t *object)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
const char *key;
|
||||
json_t *value;
|
||||
void *iter;
|
||||
|
||||
result = json_object();
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
json_object_foreach(object, key, value)
|
||||
/* Cannot use json_object_foreach because object has to be cast
|
||||
non-const */
|
||||
iter = json_object_iter((json_t *)object);
|
||||
while(iter) {
|
||||
const char *key;
|
||||
const json_t *value;
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
|
||||
json_object_set_new_nocheck(result, key, json_deep_copy(value));
|
||||
iter = json_object_iter_next((json_t *)object, iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -318,8 +352,6 @@ json_t *json_array(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
|
||||
return &array->json;
|
||||
}
|
||||
|
||||
@@ -501,7 +533,10 @@ int json_array_remove(json_t *json, size_t index)
|
||||
|
||||
json_decref(array->table[index]);
|
||||
|
||||
array_move(array, index, index + 1, array->entries - index);
|
||||
/* If we're removing the last element, nothing has to be moved */
|
||||
if(index < array->entries - 1)
|
||||
array_move(array, index, index + 1, array->entries - index - 1);
|
||||
|
||||
array->entries--;
|
||||
|
||||
return 0;
|
||||
@@ -545,7 +580,7 @@ int json_array_extend(json_t *json, json_t *other_json)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_array_equal(json_t *array1, json_t *array2)
|
||||
static int json_array_equal(const json_t *array1, const json_t *array2)
|
||||
{
|
||||
size_t i, size;
|
||||
|
||||
@@ -582,7 +617,7 @@ static json_t *json_array_copy(json_t *array)
|
||||
return result;
|
||||
}
|
||||
|
||||
static json_t *json_array_deep_copy(json_t *array)
|
||||
static json_t *json_array_deep_copy(const json_t *array)
|
||||
{
|
||||
json_t *result;
|
||||
size_t i;
|
||||
@@ -599,33 +634,68 @@ static json_t *json_array_deep_copy(json_t *array)
|
||||
|
||||
/*** string ***/
|
||||
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
static json_t *string_create(const char *value, size_t len, int own)
|
||||
{
|
||||
char *v;
|
||||
json_string_t *string;
|
||||
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string)
|
||||
return NULL;
|
||||
json_init(&string->json, JSON_STRING);
|
||||
if(own)
|
||||
v = (char *)value;
|
||||
else {
|
||||
v = jsonp_strndup(value, len);
|
||||
if(!v)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string->value = jsonp_strdup(value);
|
||||
if(!string->value) {
|
||||
jsonp_free(string);
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string) {
|
||||
if(!own)
|
||||
jsonp_free(v);
|
||||
return NULL;
|
||||
}
|
||||
json_init(&string->json, JSON_STRING);
|
||||
string->value = v;
|
||||
string->length = len;
|
||||
|
||||
return &string->json;
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_string_nocheck(value);
|
||||
return string_create(value, strlen(value), 0);
|
||||
}
|
||||
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 0);
|
||||
}
|
||||
|
||||
/* this is private; "steal" is not a public API concept */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 1);
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_stringn(value, strlen(value));
|
||||
}
|
||||
|
||||
json_t *json_stringn(const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return NULL;
|
||||
|
||||
return json_stringn_nocheck(value, len);
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *json)
|
||||
@@ -636,7 +706,23 @@ const char *json_string_value(const json_t *json)
|
||||
return json_to_string(json)->value;
|
||||
}
|
||||
|
||||
size_t json_string_length(const json_t *json)
|
||||
{
|
||||
if(!json_is_string(json))
|
||||
return 0;
|
||||
|
||||
return json_to_string(json)->length;
|
||||
}
|
||||
|
||||
int json_string_set_nocheck(json_t *json, const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn_nocheck(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
@@ -644,23 +730,32 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
if(!json_is_string(json) || !value)
|
||||
return -1;
|
||||
|
||||
dup = jsonp_strdup(value);
|
||||
dup = jsonp_strndup(value, len);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
string = json_to_string(json);
|
||||
jsonp_free(string->value);
|
||||
string->value = dup;
|
||||
string->length = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_string_set(json_t *json, const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_set_nocheck(json, value);
|
||||
return json_string_setn(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, len);
|
||||
}
|
||||
|
||||
static void json_delete_string(json_string_t *string)
|
||||
@@ -669,14 +764,61 @@ static void json_delete_string(json_string_t *string)
|
||||
jsonp_free(string);
|
||||
}
|
||||
|
||||
static int json_string_equal(json_t *string1, json_t *string2)
|
||||
static int json_string_equal(const json_t *string1, const json_t *string2)
|
||||
{
|
||||
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||
json_string_t *s1, *s2;
|
||||
|
||||
if(!json_is_string(string1) || !json_is_string(string2))
|
||||
return 0;
|
||||
|
||||
s1 = json_to_string(string1);
|
||||
s2 = json_to_string(string2);
|
||||
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
|
||||
}
|
||||
|
||||
static json_t *json_string_copy(json_t *string)
|
||||
static json_t *json_string_copy(const json_t *string)
|
||||
{
|
||||
return json_string_nocheck(json_string_value(string));
|
||||
json_string_t *s;
|
||||
|
||||
if(!json_is_string(string))
|
||||
return NULL;
|
||||
|
||||
s = json_to_string(string);
|
||||
return json_stringn_nocheck(s->value, s->length);
|
||||
}
|
||||
|
||||
json_t *json_vsprintf(const char *fmt, va_list ap) {
|
||||
int length;
|
||||
char *buf;
|
||||
va_list aq;
|
||||
va_copy(aq, ap);
|
||||
|
||||
length = vsnprintf(NULL, 0, fmt, ap);
|
||||
if (length == 0)
|
||||
return json_string("");
|
||||
|
||||
buf = jsonp_malloc(length + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
vsnprintf(buf, length + 1, fmt, aq);
|
||||
if (!utf8_check_string(buf, length)) {
|
||||
jsonp_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return jsonp_stringn_nocheck_own(buf, length);
|
||||
}
|
||||
|
||||
json_t *json_sprintf(const char *fmt, ...) {
|
||||
json_t *result;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
result = json_vsprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -716,12 +858,12 @@ static void json_delete_integer(json_integer_t *integer)
|
||||
jsonp_free(integer);
|
||||
}
|
||||
|
||||
static int json_integer_equal(json_t *integer1, json_t *integer2)
|
||||
static int json_integer_equal(const json_t *integer1, const json_t *integer2)
|
||||
{
|
||||
return json_integer_value(integer1) == json_integer_value(integer2);
|
||||
}
|
||||
|
||||
static json_t *json_integer_copy(json_t *integer)
|
||||
static json_t *json_integer_copy(const json_t *integer)
|
||||
{
|
||||
return json_integer(json_integer_value(integer));
|
||||
}
|
||||
@@ -731,7 +873,12 @@ static json_t *json_integer_copy(json_t *integer)
|
||||
|
||||
json_t *json_real(double value)
|
||||
{
|
||||
json_real_t *real = jsonp_malloc(sizeof(json_real_t));
|
||||
json_real_t *real;
|
||||
|
||||
if(isnan(value) || isinf(value))
|
||||
return NULL;
|
||||
|
||||
real = jsonp_malloc(sizeof(json_real_t));
|
||||
if(!real)
|
||||
return NULL;
|
||||
json_init(&real->json, JSON_REAL);
|
||||
@@ -750,8 +897,8 @@ double json_real_value(const json_t *json)
|
||||
|
||||
int json_real_set(json_t *json, double value)
|
||||
{
|
||||
if(!json_is_real(json))
|
||||
return 0;
|
||||
if(!json_is_real(json) || isnan(value) || isinf(value))
|
||||
return -1;
|
||||
|
||||
json_to_real(json)->value = value;
|
||||
|
||||
@@ -763,12 +910,12 @@ static void json_delete_real(json_real_t *real)
|
||||
jsonp_free(real);
|
||||
}
|
||||
|
||||
static int json_real_equal(json_t *real1, json_t *real2)
|
||||
static int json_real_equal(const json_t *real1, const json_t *real2)
|
||||
{
|
||||
return json_real_value(real1) == json_real_value(real2);
|
||||
}
|
||||
|
||||
static json_t *json_real_copy(json_t *real)
|
||||
static json_t *json_real_copy(const json_t *real)
|
||||
{
|
||||
return json_real(json_real_value(real));
|
||||
}
|
||||
@@ -779,7 +926,7 @@ static json_t *json_real_copy(json_t *real)
|
||||
double json_number_value(const json_t *json)
|
||||
{
|
||||
if(json_is_integer(json))
|
||||
return json_integer_value(json);
|
||||
return (double)json_integer_value(json);
|
||||
else if(json_is_real(json))
|
||||
return json_real_value(json);
|
||||
else
|
||||
@@ -814,20 +961,28 @@ json_t *json_null(void)
|
||||
|
||||
void json_delete(json_t *json)
|
||||
{
|
||||
if(json_is_object(json))
|
||||
json_delete_object(json_to_object(json));
|
||||
if (!json)
|
||||
return;
|
||||
|
||||
else if(json_is_array(json))
|
||||
json_delete_array(json_to_array(json));
|
||||
|
||||
else if(json_is_string(json))
|
||||
json_delete_string(json_to_string(json));
|
||||
|
||||
else if(json_is_integer(json))
|
||||
json_delete_integer(json_to_integer(json));
|
||||
|
||||
else if(json_is_real(json))
|
||||
json_delete_real(json_to_real(json));
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_OBJECT:
|
||||
json_delete_object(json_to_object(json));
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
json_delete_array(json_to_array(json));
|
||||
break;
|
||||
case JSON_STRING:
|
||||
json_delete_string(json_to_string(json));
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
json_delete_integer(json_to_integer(json));
|
||||
break;
|
||||
case JSON_REAL:
|
||||
json_delete_real(json_to_real(json));
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* json_delete is not called for true, false or null */
|
||||
}
|
||||
@@ -835,7 +990,7 @@ void json_delete(json_t *json)
|
||||
|
||||
/*** equality ***/
|
||||
|
||||
int json_equal(json_t *json1, json_t *json2)
|
||||
int json_equal(const json_t *json1, const json_t *json2)
|
||||
{
|
||||
if(!json1 || !json2)
|
||||
return 0;
|
||||
@@ -847,22 +1002,20 @@ int json_equal(json_t *json1, json_t *json2)
|
||||
if(json1 == json2)
|
||||
return 1;
|
||||
|
||||
if(json_is_object(json1))
|
||||
return json_object_equal(json1, json2);
|
||||
|
||||
if(json_is_array(json1))
|
||||
return json_array_equal(json1, json2);
|
||||
|
||||
if(json_is_string(json1))
|
||||
return json_string_equal(json1, json2);
|
||||
|
||||
if(json_is_integer(json1))
|
||||
return json_integer_equal(json1, json2);
|
||||
|
||||
if(json_is_real(json1))
|
||||
return json_real_equal(json1, json2);
|
||||
|
||||
return 0;
|
||||
switch(json_typeof(json1)) {
|
||||
case JSON_OBJECT:
|
||||
return json_object_equal(json1, json2);
|
||||
case JSON_ARRAY:
|
||||
return json_array_equal(json1, json2);
|
||||
case JSON_STRING:
|
||||
return json_string_equal(json1, json2);
|
||||
case JSON_INTEGER:
|
||||
return json_integer_equal(json1, json2);
|
||||
case JSON_REAL:
|
||||
return json_real_equal(json1, json2);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -873,52 +1026,53 @@ json_t *json_copy(json_t *json)
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
if(json_is_object(json))
|
||||
return json_object_copy(json);
|
||||
|
||||
if(json_is_array(json))
|
||||
return json_array_copy(json);
|
||||
|
||||
if(json_is_string(json))
|
||||
return json_string_copy(json);
|
||||
|
||||
if(json_is_integer(json))
|
||||
return json_integer_copy(json);
|
||||
|
||||
if(json_is_real(json))
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return json;
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_OBJECT:
|
||||
return json_object_copy(json);
|
||||
case JSON_ARRAY:
|
||||
return json_array_copy(json);
|
||||
case JSON_STRING:
|
||||
return json_string_copy(json);
|
||||
case JSON_INTEGER:
|
||||
return json_integer_copy(json);
|
||||
case JSON_REAL:
|
||||
return json_real_copy(json);
|
||||
case JSON_TRUE:
|
||||
case JSON_FALSE:
|
||||
case JSON_NULL:
|
||||
return json;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_t *json_deep_copy(json_t *json)
|
||||
json_t *json_deep_copy(const json_t *json)
|
||||
{
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
if(json_is_object(json))
|
||||
return json_object_deep_copy(json);
|
||||
|
||||
if(json_is_array(json))
|
||||
return json_array_deep_copy(json);
|
||||
|
||||
/* for the rest of the types, deep copying doesn't differ from
|
||||
shallow copying */
|
||||
|
||||
if(json_is_string(json))
|
||||
return json_string_copy(json);
|
||||
|
||||
if(json_is_integer(json))
|
||||
return json_integer_copy(json);
|
||||
|
||||
if(json_is_real(json))
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return json;
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_OBJECT:
|
||||
return json_object_deep_copy(json);
|
||||
case JSON_ARRAY:
|
||||
return json_array_deep_copy(json);
|
||||
/* for the rest of the types, deep copying doesn't differ from
|
||||
shallow copying */
|
||||
case JSON_STRING:
|
||||
return json_string_copy(json);
|
||||
case JSON_INTEGER:
|
||||
return json_integer_copy(json);
|
||||
case JSON_REAL:
|
||||
return json_real_copy(json);
|
||||
case JSON_TRUE:
|
||||
case JSON_FALSE:
|
||||
case JSON_NULL:
|
||||
return (json_t *)json;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
6
test/.gitignore
vendored
6
test/.gitignore
vendored
@@ -7,10 +7,16 @@ suites/api/test_dump
|
||||
suites/api/test_dump_callback
|
||||
suites/api/test_equal
|
||||
suites/api/test_load
|
||||
suites/api/test_load_callback
|
||||
suites/api/test_loadb
|
||||
suites/api/test_memory_funcs
|
||||
suites/api/test_number
|
||||
suites/api/test_object
|
||||
suites/api/test_pack
|
||||
suites/api/test_simple
|
||||
suites/api/test_sprintf
|
||||
suites/api/test_unpack
|
||||
run-suites.log
|
||||
run-suites.trs
|
||||
test-suite.log
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
check_PROGRAMS = json_process
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = $(top_builddir)/src/libjansson.la
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -15,17 +15,230 @@
|
||||
#include <ctype.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#if HAVE_LOCALE_H
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
#include <io.h> /* for _setmode() */
|
||||
#include <fcntl.h> /* for _O_BINARY */
|
||||
|
||||
static const char dir_sep = '\\';
|
||||
#else
|
||||
static const char dir_sep = '/';
|
||||
#endif
|
||||
|
||||
|
||||
struct config {
|
||||
int indent;
|
||||
int compact;
|
||||
int preserve_order;
|
||||
int ensure_ascii;
|
||||
int sort_keys;
|
||||
int strip;
|
||||
int use_env;
|
||||
int have_hashseed;
|
||||
int hashseed;
|
||||
int precision;
|
||||
} conf;
|
||||
|
||||
#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t')
|
||||
|
||||
/* 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)
|
||||
{
|
||||
size_t length;
|
||||
char *result = str;
|
||||
while (*result && l_isspace(*result))
|
||||
result++;
|
||||
|
||||
length = strlen(result);
|
||||
if (length == 0)
|
||||
return result;
|
||||
|
||||
while (l_isspace(result[length - 1]))
|
||||
result[--length] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static char *loadfile(FILE *file)
|
||||
{
|
||||
long fsize, ret;
|
||||
char *buf;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
fsize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
buf = malloc(fsize+1);
|
||||
ret = fread(buf, 1, fsize, file);
|
||||
if (ret != fsize)
|
||||
exit(1);
|
||||
buf[fsize] = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static void read_conf(FILE *conffile)
|
||||
{
|
||||
char *buffer, *line, *val;
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
*val++ = '\0';
|
||||
|
||||
if (!strcmp(line, "JSON_INDENT"))
|
||||
conf.indent = atoi(val);
|
||||
if (!strcmp(line, "JSON_COMPACT"))
|
||||
conf.compact = atoi(val);
|
||||
if (!strcmp(line, "JSON_ENSURE_ASCII"))
|
||||
conf.ensure_ascii = atoi(val);
|
||||
if (!strcmp(line, "JSON_PRESERVE_ORDER"))
|
||||
conf.preserve_order = atoi(val);
|
||||
if (!strcmp(line, "JSON_SORT_KEYS"))
|
||||
conf.sort_keys = atoi(val);
|
||||
if (!strcmp(line, "JSON_REAL_PRECISION"))
|
||||
conf.precision = atoi(val);
|
||||
if (!strcmp(line, "STRIP"))
|
||||
conf.strip = atoi(val);
|
||||
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)
|
||||
{
|
||||
char filename[1024], *buffer;
|
||||
int ret;
|
||||
FILE *file;
|
||||
|
||||
sprintf(filename, "%s%c%s", path, dir_sep, fname);
|
||||
file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
if (conf.strip)
|
||||
strcat(filename, ".strip");
|
||||
else
|
||||
strcat(filename, ".normal");
|
||||
file = fopen(filename, "rb");
|
||||
}
|
||||
if (!file) {
|
||||
printf("Error: test result file could not be opened.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
buffer = loadfile(file);
|
||||
if (strcmp(buffer, str) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
free(buffer);
|
||||
fclose(file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int use_conf(char *test_path)
|
||||
{
|
||||
int ret;
|
||||
size_t flags = 0;
|
||||
char filename[1024], errstr[1024];
|
||||
char *buffer;
|
||||
FILE *infile, *conffile;
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
sprintf(filename, "%s%cinput", test_path, dir_sep);
|
||||
if (!(infile = fopen(filename, "rb"))) {
|
||||
fprintf(stderr, "Could not open \"%s\"\n", filename);
|
||||
return 2;
|
||||
}
|
||||
|
||||
sprintf(filename, "%s%cenv", test_path, dir_sep);
|
||||
conffile = fopen(filename, "rb");
|
||||
if (conffile) {
|
||||
read_conf(conffile);
|
||||
fclose(conffile);
|
||||
}
|
||||
|
||||
if (conf.indent < 0 || conf.indent > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent);
|
||||
fclose(infile);
|
||||
return 2;
|
||||
}
|
||||
if (conf.indent)
|
||||
flags |= JSON_INDENT(conf.indent);
|
||||
|
||||
if (conf.compact)
|
||||
flags |= JSON_COMPACT;
|
||||
|
||||
if (conf.ensure_ascii)
|
||||
flags |= JSON_ENSURE_ASCII;
|
||||
|
||||
if (conf.preserve_order)
|
||||
flags |= JSON_PRESERVE_ORDER;
|
||||
|
||||
if (conf.sort_keys)
|
||||
flags |= JSON_SORT_KEYS;
|
||||
|
||||
if (conf.precision < 0 || conf.precision > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
|
||||
conf.precision);
|
||||
fclose(infile);
|
||||
return 2;
|
||||
}
|
||||
if (conf.precision)
|
||||
flags |= JSON_REAL_PRECISION(conf.precision);
|
||||
|
||||
if (conf.have_hashseed)
|
||||
json_object_seed(conf.hashseed);
|
||||
|
||||
if (conf.strip) {
|
||||
/* Load to memory, strip leading and trailing whitespace */
|
||||
buffer = loadfile(infile);
|
||||
json = json_loads(strip(buffer), 0, &error);
|
||||
free(buffer);
|
||||
}
|
||||
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,
|
||||
error.text);
|
||||
|
||||
ret = cmpfile(errstr, test_path, "error");
|
||||
return ret;
|
||||
}
|
||||
|
||||
buffer = json_dumps(json, flags);
|
||||
ret = cmpfile(buffer, test_path, "output");
|
||||
free(buffer);
|
||||
json_decref(json);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int getenv_int(const char *name)
|
||||
{
|
||||
char *value, *end;
|
||||
@@ -42,56 +255,25 @@ static int getenv_int(const char *name)
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
int use_env()
|
||||
{
|
||||
size_t length;
|
||||
char *result = str;
|
||||
while(*result && l_isspace(*result))
|
||||
result++;
|
||||
|
||||
length = strlen(result);
|
||||
if(length == 0)
|
||||
return result;
|
||||
|
||||
while(l_isspace(result[length - 1]))
|
||||
result[--length] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int indent = 0;
|
||||
int indent, precision;
|
||||
size_t flags = 0;
|
||||
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
#if HAVE_SETLOCALE
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
/* On Windows, set stdout and stderr to binary mode to avoid
|
||||
outputting DOS line terminators */
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
_setmode(_fileno(stderr), _O_BINARY);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
indent = getenv_int("JSON_INDENT");
|
||||
if(indent < 0 || indent > 255) {
|
||||
if(indent < 0 || indent > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(indent > 0)
|
||||
flags |= JSON_INDENT(indent);
|
||||
|
||||
@@ -107,20 +289,35 @@ int main(int argc, char *argv[])
|
||||
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;
|
||||
char *buffer = NULL, *buf_ck = NULL;
|
||||
|
||||
while(1) {
|
||||
int count;
|
||||
size_t count;
|
||||
|
||||
size = (size == 0 ? 128 : size * 2);
|
||||
buffer = realloc(buffer, size);
|
||||
if(!buffer) {
|
||||
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) {
|
||||
@@ -138,8 +335,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d %d %d\n%s\n",
|
||||
error.line, error.column, error.position,
|
||||
error.text);
|
||||
error.line, error.column,
|
||||
error.position, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -148,3 +345,41 @@ int main(int argc, char *argv[])
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
char *test_path = NULL;
|
||||
|
||||
#ifdef HAVE_SETLOCALE
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
|
||||
if (argc < 2) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "argc =%d\n", argc);
|
||||
fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -34,15 +34,15 @@ failed=0
|
||||
for suite in $SUITES; do
|
||||
echo "Suite: $suite"
|
||||
if $suites_srcdir/$suite/run $suite; then
|
||||
passed=$(($passed+1))
|
||||
passed=`expr $passed + 1`
|
||||
else
|
||||
failed=$(($failed+1))
|
||||
failed=`expr $failed + 1`
|
||||
[ $STOP -eq 1 ] && break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
echo "$failed of $((passed+failed)) test suites failed"
|
||||
echo "$failed of `expr $passed + $failed` test suites failed"
|
||||
exit 1
|
||||
else
|
||||
echo "$passed test suites passed"
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
# Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
die() {
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "$1" ] || die "Usage: $0 suite-name"
|
||||
[ -n "$bindir" ] || die "Set bindir"
|
||||
[ -n "$logdir" ] || die "Set logdir"
|
||||
[ -n "$scriptdir" ] || die "Set scriptdir"
|
||||
[ -n "$suites_srcdir" ] || die "Set suites_srcdir"
|
||||
[ -n "$suites_builddir" ] || die "Set suites_builddir"
|
||||
|
||||
json_process=$bindir/json_process
|
||||
|
||||
suite_name=$1
|
||||
@@ -10,7 +22,6 @@ suite_srcdir=$suites_srcdir/$suite_name
|
||||
suite_builddir=$suites_builddir/$suite_name
|
||||
suite_log=$logdir/$suite_name
|
||||
|
||||
|
||||
[ -z "$VERBOSE" ] && VERBOSE=0
|
||||
[ -z "$STOP" ] && STOP=0
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
2
test/suites/.gitattributes
vendored
Normal file
2
test/suites/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
api/ text=auto
|
||||
* text eol=lf
|
||||
@@ -8,11 +8,13 @@ check_PROGRAMS = \
|
||||
test_equal \
|
||||
test_load \
|
||||
test_loadb \
|
||||
test_load_callback \
|
||||
test_memory_funcs \
|
||||
test_number \
|
||||
test_object \
|
||||
test_pack \
|
||||
test_simple \
|
||||
test_sprintf \
|
||||
test_unpack
|
||||
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
@@ -26,9 +28,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
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = $(top_builddir)/src/libjansson.la
|
||||
|
||||
@@ -1,102 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# This test checks that libjansson.so exports the correct symbols.
|
||||
|
||||
# The list of symbols that the shared object should export
|
||||
sort >$test_log/exports <<EOF
|
||||
json_delete
|
||||
json_true
|
||||
json_false
|
||||
json_null
|
||||
json_string
|
||||
json_string_nocheck
|
||||
json_string_value
|
||||
json_string_set
|
||||
json_string_set_nocheck
|
||||
json_integer
|
||||
json_integer_value
|
||||
json_integer_set
|
||||
json_real
|
||||
json_real_value
|
||||
json_real_set
|
||||
json_number_value
|
||||
json_array
|
||||
json_array_size
|
||||
json_array_get
|
||||
json_array_set_new
|
||||
json_array_append_new
|
||||
json_array_insert_new
|
||||
json_array_remove
|
||||
json_array_clear
|
||||
json_array_extend
|
||||
json_object
|
||||
json_object_size
|
||||
json_object_get
|
||||
json_object_set_new
|
||||
json_object_set_new_nocheck
|
||||
json_object_del
|
||||
json_object_clear
|
||||
json_object_update
|
||||
json_object_update_existing
|
||||
json_object_update_missing
|
||||
json_object_iter
|
||||
json_object_iter_at
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_object_key_to_iter
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
json_dump_callback
|
||||
json_loads
|
||||
json_loadf
|
||||
json_load_file
|
||||
json_loadb
|
||||
json_equal
|
||||
json_copy
|
||||
json_deep_copy
|
||||
json_pack
|
||||
json_pack_ex
|
||||
json_vpack_ex
|
||||
json_unpack
|
||||
json_unpack_ex
|
||||
json_vunpack_ex
|
||||
json_set_alloc_funcs
|
||||
EOF
|
||||
|
||||
# The list of functions are not exported in the library because they
|
||||
# are macros or static inline functions. This is only the make the
|
||||
# list complete, there are not used by the test.
|
||||
sort >$test_log/macros_or_inline <<EOF
|
||||
json_typeof
|
||||
json_incref
|
||||
json_decref
|
||||
json_is_object
|
||||
json_is_object
|
||||
json_is_array
|
||||
json_is_string
|
||||
json_is_integer
|
||||
json_is_real
|
||||
json_is_true
|
||||
json_is_false
|
||||
json_is_null
|
||||
json_is_number
|
||||
json_is_boolean
|
||||
json_array_set
|
||||
json_array_append
|
||||
json_array_insert
|
||||
json_object_set
|
||||
json_object_set_nocheck
|
||||
EOF
|
||||
#
|
||||
|
||||
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 \
|
||||
| sed -e 's/ //g' \
|
||||
| sort \
|
||||
>$test_log/exports
|
||||
|
||||
nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \
|
||||
|| exit 77 # Skip if "nm -D" doesn't seem to work
|
||||
|
||||
grep ' T ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
|
||||
grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | grep -v '^_' | sort >$test_log/output
|
||||
|
||||
if ! cmp -s $test_log/exports $test_log/output; then
|
||||
diff -u $test_log/exports $test_log/output >&2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -11,7 +11,7 @@
|
||||
static void test_misc(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *value;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
@@ -206,6 +206,7 @@ static void test_insert(void)
|
||||
static void test_remove(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
@@ -253,6 +254,19 @@ static void test_remove(void)
|
||||
json_array_get(array, 2) != seven)
|
||||
fail("remove works incorrectly");
|
||||
|
||||
json_decref(array);
|
||||
|
||||
array = json_array();
|
||||
for(i = 0; i < 4; i++) {
|
||||
json_array_append(array, five);
|
||||
json_array_append(array, seven);
|
||||
}
|
||||
if(json_array_size(array) != 8)
|
||||
fail("unable to append 8 items to array");
|
||||
|
||||
/* Remove an element from a "full" array. */
|
||||
json_array_remove(array, 5);
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
@@ -386,6 +400,25 @@ static void test_circular()
|
||||
json_decref(array1);
|
||||
}
|
||||
|
||||
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))
|
||||
fail("json_array_foreach failed to iterate all elements");
|
||||
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
}
|
||||
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
@@ -395,4 +428,5 @@ static void run_tests()
|
||||
test_clear();
|
||||
test_extend();
|
||||
test_circular();
|
||||
test_array_foreach();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -232,6 +232,9 @@ static void test_copy_object(void)
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
const char *keys[] = {"foo", "a", "b", "c"};
|
||||
int i;
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
@@ -247,6 +250,7 @@ static void test_copy_object(void)
|
||||
if(!json_equal(copy, object))
|
||||
fail("copying an object produces an inequal copy");
|
||||
|
||||
i = 0;
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
@@ -258,9 +262,13 @@ static void test_copy_object(void)
|
||||
value2 = json_object_get(copy, key);
|
||||
|
||||
if(value1 != value2)
|
||||
fail("deep copying an object modifies its items");
|
||||
fail("copying an object modifies its items");
|
||||
|
||||
if (strcmp(key, keys[i]) != 0)
|
||||
fail("copying an object doesn't preserve key order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
@@ -272,6 +280,9 @@ static void test_deep_copy_object(void)
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
const char *keys[] = {"foo", "a", "b", "c"};
|
||||
int i;
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
@@ -287,6 +298,7 @@ static void test_deep_copy_object(void)
|
||||
if(!json_equal(copy, object))
|
||||
fail("deep copying an object produces an inequal copy");
|
||||
|
||||
i = 0;
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
@@ -300,7 +312,11 @@ static void test_deep_copy_object(void)
|
||||
if(value1 == value2)
|
||||
fail("deep copying an object doesn't copy its items");
|
||||
|
||||
if (strcmp(key, keys[i]) != 0)
|
||||
fail("deep copying an object doesn't preserve key order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
|
||||
@@ -1,14 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "jansson_private_config.h"
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "util.h"
|
||||
|
||||
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)
|
||||
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_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1)
|
||||
fail("json_dumpf didn't fail for NULL");
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if(json_dumpfd(NULL, STDERR_FILENO, JSON_ENCODE_ANY) != -1)
|
||||
fail("json_dumpfd didn't fail for NULL");
|
||||
#endif
|
||||
|
||||
/* Don't test json_dump_file to avoid creating a file */
|
||||
|
||||
if(json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1)
|
||||
fail("json_dump_callback didn't fail for NULL");
|
||||
}
|
||||
|
||||
|
||||
static void encode_twice()
|
||||
{
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
@@ -101,14 +137,15 @@ static void encode_other_than_array_or_object()
|
||||
* succeed if the JSON_ENCODE_ANY flag is used */
|
||||
|
||||
json_t *json;
|
||||
FILE *fp = NULL;
|
||||
char *result;
|
||||
|
||||
json = json_string("foo");
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded a string!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
if(json_dumpf(json, NULL, 0) == 0)
|
||||
fail("json_dumpf encoded a string!");
|
||||
if(json_dumpfd(json, -1, 0) == 0)
|
||||
fail("json_dumpfd encoded a string!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "\"foo\"") != 0)
|
||||
@@ -120,8 +157,10 @@ static void encode_other_than_array_or_object()
|
||||
json = json_integer(42);
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded an integer!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
if(json_dumpf(json, NULL, 0) == 0)
|
||||
fail("json_dumpf encoded an integer!");
|
||||
if(json_dumpfd(json, -1, 0) == 0)
|
||||
fail("json_dumpfd encoded an integer!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "42") != 0)
|
||||
@@ -133,9 +172,155 @@ static void encode_other_than_array_or_object()
|
||||
|
||||
}
|
||||
|
||||
static void escape_slashes()
|
||||
{
|
||||
/* Test dump escaping slashes */
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_object();
|
||||
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\"}"))
|
||||
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\"}"))
|
||||
fail("json_dumps failed to escape slashes");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
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))
|
||||
fail("json_dumps failed to dump an embedded NUL byte");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void dump_file()
|
||||
{
|
||||
json_t *json;
|
||||
int result;
|
||||
|
||||
result = json_dump_file(NULL, "", 0);
|
||||
if (result != -1)
|
||||
fail("json_dump_file succeeded with invalid args");
|
||||
|
||||
json = json_object();
|
||||
result = json_dump_file(json, "json_dump_file.json", 0);
|
||||
if (result != 0)
|
||||
fail("json_dump_file failed");
|
||||
|
||||
json_decref(json);
|
||||
remove("json_dump_file.json");
|
||||
}
|
||||
|
||||
static void dumpb()
|
||||
{
|
||||
char buf[2];
|
||||
json_t *obj;
|
||||
size_t size;
|
||||
|
||||
obj = json_object();
|
||||
|
||||
size = json_dumpb(obj, buf, sizeof(buf), 0);
|
||||
if(size != 2 || strncmp(buf, "{}", 2))
|
||||
fail("json_dumpb failed");
|
||||
|
||||
json_decref(obj);
|
||||
obj = json_pack("{s:s}", "foo", "bar");
|
||||
|
||||
size = json_dumpb(obj, buf, sizeof(buf), JSON_COMPACT);
|
||||
if(size != 13)
|
||||
fail("json_dumpb size check failed");
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void dumpfd()
|
||||
{
|
||||
#ifdef HAVE_UNISTD_H
|
||||
int fds[2] = {-1, -1};
|
||||
json_t *a, *b;
|
||||
|
||||
if(pipe(fds))
|
||||
fail("pipe() failed");
|
||||
|
||||
a = json_pack("{s:s}", "foo", "bar");
|
||||
|
||||
if(json_dumpfd(a, fds[1], 0))
|
||||
fail("json_dumpfd() failed");
|
||||
close(fds[1]);
|
||||
|
||||
b = json_loadfd(fds[0], 0, NULL);
|
||||
if (!b)
|
||||
fail("json_loadfd() failed");
|
||||
close(fds[0]);
|
||||
|
||||
if (!json_equal(a, b))
|
||||
fail("json_equal() failed for fd test");
|
||||
|
||||
json_decref(a);
|
||||
json_decref(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void embed()
|
||||
{
|
||||
static const char *plains[] = {
|
||||
"{\"bar\":[],\"foo\":{}}",
|
||||
"[[],{}]",
|
||||
"{}",
|
||||
"[]",
|
||||
NULL
|
||||
};
|
||||
|
||||
size_t i;
|
||||
|
||||
for(i = 0; plains[i]; i++) {
|
||||
const char *plain = plains[i];
|
||||
json_t *parse = NULL;
|
||||
char *embed = NULL;
|
||||
size_t psize = 0;
|
||||
size_t esize = 0;
|
||||
|
||||
psize = strlen(plain) - 2;
|
||||
embed = calloc(1, psize);
|
||||
parse = json_loads(plain, 0, NULL);
|
||||
esize = json_dumpb(parse, embed, psize,
|
||||
JSON_COMPACT | JSON_SORT_KEYS | JSON_EMBED);
|
||||
json_decref(parse);
|
||||
if(esize != psize)
|
||||
fail("json_dumpb(JSON_EMBED) returned an invalid size");
|
||||
if(strncmp(plain + 1, embed, esize) != 0)
|
||||
fail("json_dumps(JSON_EMBED) returned an invalid value");
|
||||
free(embed);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
encode_null();
|
||||
encode_twice();
|
||||
circular_references();
|
||||
encode_other_than_array_or_object();
|
||||
escape_slashes();
|
||||
encode_nul_byte();
|
||||
dump_file();
|
||||
dumpb();
|
||||
dumpfd();
|
||||
embed();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -32,6 +32,24 @@ static void file_not_found()
|
||||
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
if(json_error_code(&error) != json_error_cannot_open_file)
|
||||
fail("json_load_file returned an invalid error code");
|
||||
}
|
||||
|
||||
static void very_long_file_name() {
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, &error);
|
||||
if(json)
|
||||
fail("json_load_file returned non-NULL for a nonexistent file");
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
|
||||
if (strncmp(error.source, "...aaa", 6) != 0)
|
||||
fail("error source was set incorrectly");
|
||||
if(json_error_code(&error) != json_error_cannot_open_file)
|
||||
fail("error code was set incorrectly");
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
@@ -40,7 +58,7 @@ static void reject_duplicates()
|
||||
|
||||
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
|
||||
fail("json_loads did not detect a duplicate key");
|
||||
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
check_error(json_error_duplicate_key, "duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
}
|
||||
|
||||
static void disable_eof_check()
|
||||
@@ -52,7 +70,7 @@ static void disable_eof_check()
|
||||
|
||||
if(json_loads(text, 0, &error))
|
||||
fail("json_loads did not detect garbage after JSON text");
|
||||
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
|
||||
check_error(json_error_end_of_input_expected, "end of file expected near 'garbage'", "<string>", 1, 18, 18);
|
||||
|
||||
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
|
||||
if(!json)
|
||||
@@ -87,6 +105,69 @@ static void decode_any()
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void decode_int_as_real()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
#if JSON_INTEGER_IS_LONG_LONG
|
||||
const char *imprecise;
|
||||
json_int_t expected;
|
||||
#endif
|
||||
|
||||
char big[311];
|
||||
|
||||
json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
|
||||
if (!json || !json_is_real(json) || json_real_value(json) != 42.0)
|
||||
fail("json_load decode int as real failed - int");
|
||||
json_decref(json);
|
||||
|
||||
#if JSON_INTEGER_IS_LONG_LONG
|
||||
/* This number cannot be represented exactly by a double */
|
||||
imprecise = "9007199254740993";
|
||||
expected = 9007199254740992ll;
|
||||
|
||||
json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY,
|
||||
&error);
|
||||
if (!json || !json_is_real(json) || expected != (json_int_t)json_real_value(json))
|
||||
fail("json_load decode int as real failed - expected imprecision");
|
||||
json_decref(json);
|
||||
#endif
|
||||
|
||||
/* 1E309 overflows. Here we create 1E309 as a decimal number, i.e.
|
||||
1000...(309 zeroes)...0. */
|
||||
big[0] = '1';
|
||||
memset(big + 1, '0', 309);
|
||||
big[310] = '\0';
|
||||
|
||||
json = json_loads(big, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
|
||||
if (json || strcmp(error.text, "real number overflow") != 0 ||
|
||||
json_error_code(&error) != json_error_numeric_overflow)
|
||||
fail("json_load decode int as real failed - expected overflow");
|
||||
json_decref(json);
|
||||
|
||||
}
|
||||
|
||||
static void allow_nul()
|
||||
{
|
||||
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))
|
||||
fail("unable to decode embedded NUL byte");
|
||||
|
||||
if(json_string_length(json) != len)
|
||||
fail("decoder returned wrong string length");
|
||||
|
||||
if(memcmp(json_string_value(json), expected, len + 1))
|
||||
fail("decoder returned wrong string content");
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void load_wrong_args()
|
||||
{
|
||||
json_t *json;
|
||||
@@ -104,9 +185,13 @@ static void load_wrong_args()
|
||||
if (json)
|
||||
fail("json_loadf should return NULL if the first argument is NULL");
|
||||
|
||||
json = json_loadfd(-1, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadfd should return NULL if the first argument is < 0");
|
||||
|
||||
json = json_load_file(NULL, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadf should return NULL if the first argument is NULL");
|
||||
fail("json_load_file should return NULL if the first argument is NULL");
|
||||
}
|
||||
|
||||
static void position()
|
||||
@@ -126,12 +211,36 @@ static void position()
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void error_code()
|
||||
{
|
||||
json_error_t error;
|
||||
json_t *json = json_loads("[123] garbage", 0, &error);
|
||||
if(json != NULL)
|
||||
fail("json_loads returned not NULL");
|
||||
if(strlen(error.text) >= JSON_ERROR_TEXT_LENGTH)
|
||||
fail("error.text longer than expected");
|
||||
if(json_error_code(&error) != json_error_end_of_input_expected)
|
||||
fail("json_loads returned incorrect error code");
|
||||
|
||||
json = json_loads("{\"foo\": ", 0, &error);
|
||||
if(json != NULL)
|
||||
fail("json_loads returned not NULL");
|
||||
if(strlen(error.text) >= JSON_ERROR_TEXT_LENGTH)
|
||||
fail("error.text longer than expected");
|
||||
if(json_error_code(&error) != json_error_premature_end_of_input)
|
||||
fail("json_loads returned incorrect error code");
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
file_not_found();
|
||||
very_long_file_name();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
decode_any();
|
||||
decode_int_as_real();
|
||||
allow_nul();
|
||||
load_wrong_args();
|
||||
position();
|
||||
error_code();
|
||||
}
|
||||
|
||||
75
test/suites/api/test_load_callback.c
Normal file
75
test/suites/api/test_load_callback.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "util.h"
|
||||
|
||||
struct my_source {
|
||||
const char *buf;
|
||||
size_t off;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct my_source *s = arg;
|
||||
if (buflen > s->cap - s->off)
|
||||
buflen = s->cap - s->off;
|
||||
if (buflen > 0) {
|
||||
memcpy(buf, s->buf + s->off, buflen);
|
||||
s->off += buflen;
|
||||
return buflen;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
struct my_source s;
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
s.off = 0;
|
||||
s.cap = strlen(my_str);
|
||||
s.buf = my_str;
|
||||
|
||||
json = json_load_callback(greedy_reader, &s, 0, &error);
|
||||
|
||||
if (!json)
|
||||
fail("json_load_callback failed on a valid callback");
|
||||
json_decref(json);
|
||||
|
||||
s.off = 0;
|
||||
s.cap = strlen(my_str) - 1;
|
||||
s.buf = my_str;
|
||||
|
||||
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");
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
if (strcmp(error.text, "wrong arguments") != 0) {
|
||||
fail("json_load_callback returned an invalid error message for a NULL load callback");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
static int malloc_called = 0;
|
||||
static int free_called = 0;
|
||||
static size_t malloc_used = 0;
|
||||
|
||||
/* helper */
|
||||
/* helpers */
|
||||
static void create_and_free_complex_object()
|
||||
{
|
||||
json_t *obj;
|
||||
|
||||
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]",
|
||||
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]}",
|
||||
"foo", 42,
|
||||
"bar",
|
||||
"baz", 1,
|
||||
@@ -22,28 +23,74 @@ static void create_and_free_complex_object()
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void create_and_free_object_with_oom()
|
||||
{
|
||||
int i;
|
||||
char key[4];
|
||||
json_t *obj = json_object();
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
snprintf(key, sizeof key, "%d", i);
|
||||
json_object_set_new(obj, key, json_integer(i));
|
||||
}
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void *my_malloc(size_t size)
|
||||
{
|
||||
malloc_called += 1;
|
||||
malloc_called = 1;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void my_free(void *ptr)
|
||||
{
|
||||
free_called += 1;
|
||||
free_called = 1;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void test_simple()
|
||||
{
|
||||
json_malloc_t mfunc = NULL;
|
||||
json_free_t ffunc = NULL;
|
||||
|
||||
json_set_alloc_funcs(my_malloc, my_free);
|
||||
json_get_alloc_funcs(&mfunc, &ffunc);
|
||||
create_and_free_complex_object();
|
||||
|
||||
if(malloc_called != 20 || free_called != 20)
|
||||
if (malloc_called != 1 || free_called != 1
|
||||
|| mfunc != my_malloc || ffunc != my_free)
|
||||
fail("Custom allocation failed");
|
||||
}
|
||||
|
||||
|
||||
static void *oom_malloc(size_t size)
|
||||
{
|
||||
if (malloc_used + size > 800)
|
||||
return NULL;
|
||||
|
||||
malloc_used += size;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void oom_free(void *ptr)
|
||||
{
|
||||
free_called++;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void test_oom()
|
||||
{
|
||||
free_called = 0;
|
||||
json_set_alloc_funcs(oom_malloc, oom_free);
|
||||
create_and_free_object_with_oom();
|
||||
|
||||
if (free_called == 0)
|
||||
fail("Allocation with OOM failed");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Test the secure memory functions code given in the API reference
|
||||
documentation, but by using plain memset instead of
|
||||
@@ -55,17 +102,17 @@ 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 ptr + 8;
|
||||
return (char *)ptr + 8;
|
||||
}
|
||||
|
||||
static void secure_free(void *ptr)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
ptr -= 8;
|
||||
ptr = (char *)ptr - 8;
|
||||
size = *((size_t *)ptr);
|
||||
|
||||
/*guaranteed_*/memset(ptr, 0, size);
|
||||
/*guaranteed_*/memset(ptr, 0, size + 8);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
@@ -79,4 +126,5 @@ static void run_tests()
|
||||
{
|
||||
test_simple();
|
||||
test_secure_funcs();
|
||||
test_oom();
|
||||
}
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
#ifdef INFINITY
|
||||
// This test triggers "warning C4756: overflow in constant arithmetic"
|
||||
// in Visual Studio. This warning is triggered here by design, so disable it.
|
||||
// (This can only be done on function level so we keep these tests separate)
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning (disable: 4756)
|
||||
#endif
|
||||
static void test_inifity()
|
||||
{
|
||||
json_t *real = json_real(INFINITY);
|
||||
if (real != NULL)
|
||||
fail("could construct a real from Inf");
|
||||
|
||||
real = json_real(1.0);
|
||||
if (json_real_set(real, INFINITY) != -1)
|
||||
fail("could set a real to Inf");
|
||||
|
||||
if (json_real_value(real) != 1.0)
|
||||
fail("real value changed unexpectedly");
|
||||
|
||||
json_decref(real);
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
#endif // INFINITY
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *integer, *real;
|
||||
int i;
|
||||
json_int_t i;
|
||||
double d;
|
||||
|
||||
integer = json_integer(5);
|
||||
@@ -39,4 +68,23 @@ static void run_tests()
|
||||
|
||||
json_decref(integer);
|
||||
json_decref(real);
|
||||
|
||||
#ifdef NAN
|
||||
real = json_real(NAN);
|
||||
if(real != NULL)
|
||||
fail("could construct a real from NaN");
|
||||
|
||||
real = json_real(1.0);
|
||||
if(json_real_set(real, NAN) != -1)
|
||||
fail("could set a real to NaN");
|
||||
|
||||
if(json_real_value(real) != 1.0)
|
||||
fail("real value changed unexpectedly");
|
||||
|
||||
json_decref(real);
|
||||
#endif
|
||||
|
||||
#ifdef INFINITY
|
||||
test_inifity();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -94,7 +94,7 @@ static void test_update()
|
||||
/* perform the same update again */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
fail("unable to update a non-empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
@@ -139,6 +139,32 @@ static void test_update()
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_set_many_keys()
|
||||
{
|
||||
json_t *object, *value;
|
||||
const char *keys = "abcdefghijklmnopqrstuvwxyz";
|
||||
char buf[2];
|
||||
size_t i;
|
||||
|
||||
object = json_object();
|
||||
if (!object)
|
||||
fail("unable to create object");
|
||||
|
||||
value = json_string("a");
|
||||
if (!value)
|
||||
fail("unable to create string");
|
||||
|
||||
buf[1] = '\0';
|
||||
for (i = 0; i < strlen(keys); i++) {
|
||||
buf[0] = keys[i];
|
||||
if (json_object_set(object, buf, value))
|
||||
fail("unable to set object key");
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
json_decref(value);
|
||||
}
|
||||
|
||||
static void test_conditional_updates()
|
||||
{
|
||||
json_t *object, *other;
|
||||
@@ -262,7 +288,7 @@ static void test_iterators()
|
||||
foo = json_string("foo");
|
||||
bar = json_string("bar");
|
||||
baz = json_string("baz");
|
||||
if(!object || !foo || !bar || !bar)
|
||||
if(!object || !foo || !bar || !baz)
|
||||
fail("unable to create values");
|
||||
|
||||
if(json_object_iter_next(object, NULL))
|
||||
@@ -276,26 +302,26 @@ static void test_iterators()
|
||||
iter = json_object_iter(object);
|
||||
if(!iter)
|
||||
fail("unable to get iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "a"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != foo)
|
||||
fail("iterating failed: wrong value");
|
||||
if (strcmp(json_object_iter_key(iter), "a") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != foo)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "b"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
if (strcmp(json_object_iter_key(iter), "b") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != bar)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
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)
|
||||
fail("able to iterate over the end");
|
||||
@@ -312,22 +338,14 @@ static void test_iterators()
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
if(json_object_iter_set(object, iter, bar))
|
||||
if(json_object_iter_set(object, iter, baz))
|
||||
fail("unable to set value at iterator");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
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) != bar)
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("json_object_iter_value() fails after json_object_iter_set()");
|
||||
if(json_object_get(object, "c") != bar)
|
||||
if(json_object_get(object, "b") != baz)
|
||||
fail("json_object_get() fails after json_object_iter_set()");
|
||||
|
||||
json_decref(object);
|
||||
@@ -358,6 +376,12 @@ static void test_misc()
|
||||
if(!json_object_set(object, NULL, string))
|
||||
fail("able to set NULL key");
|
||||
|
||||
if(json_object_del(object, "a"))
|
||||
fail("unable to del the only key");
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_object_set(object, "a", NULL))
|
||||
fail("able to set NULL value");
|
||||
|
||||
@@ -479,7 +503,7 @@ static void test_preserve_order()
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_foreach()
|
||||
static void test_object_foreach()
|
||||
{
|
||||
const char *key;
|
||||
json_t *object1, *object2, *value;
|
||||
@@ -497,15 +521,35 @@ static void test_foreach()
|
||||
json_decref(object2);
|
||||
}
|
||||
|
||||
static void test_object_foreach_safe()
|
||||
{
|
||||
const char *key;
|
||||
void *tmp;
|
||||
json_t *object, *value;
|
||||
|
||||
object = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
|
||||
|
||||
json_object_foreach_safe(object, tmp, key, value) {
|
||||
json_object_del(object, key);
|
||||
}
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("json_object_foreach_safe failed to iterate all key-value pairs");
|
||||
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
test_update();
|
||||
test_set_many_keys();
|
||||
test_conditional_updates();
|
||||
test_circular();
|
||||
test_set_nocheck();
|
||||
test_iterators();
|
||||
test_preserve_order();
|
||||
test_foreach();
|
||||
test_object_foreach();
|
||||
test_object_foreach_safe();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <jansson_config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include <stdio.h>
|
||||
@@ -15,17 +21,17 @@ static void run_tests()
|
||||
{
|
||||
json_t *value;
|
||||
int i;
|
||||
char buffer[4] = {'t', 'e', 's', 't'};
|
||||
json_error_t error;
|
||||
|
||||
/*
|
||||
* Simple, valid json_pack cases
|
||||
*/
|
||||
|
||||
/* true */
|
||||
value = json_pack("b", 1);
|
||||
if(!json_is_true(value))
|
||||
fail("json_pack boolean failed");
|
||||
if(value->refcount != (ssize_t)-1)
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack boolean refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -33,7 +39,7 @@ static void run_tests()
|
||||
value = json_pack("b", 0);
|
||||
if(!json_is_false(value))
|
||||
fail("json_pack boolean failed");
|
||||
if(value->refcount != (ssize_t)-1)
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack boolean refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -41,7 +47,7 @@ static void run_tests()
|
||||
value = json_pack("n");
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack null failed");
|
||||
if(value->refcount != (ssize_t)-1)
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack null refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -49,7 +55,7 @@ static void run_tests()
|
||||
value = json_pack("i", 1);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack integer failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack integer refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -57,7 +63,7 @@ static void run_tests()
|
||||
value = json_pack("I", (json_int_t)555555);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 555555)
|
||||
fail("json_pack json_int_t failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack integer refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -65,7 +71,7 @@ static void run_tests()
|
||||
value = json_pack("f", 1.0);
|
||||
if(!json_is_real(value) || json_real_value(value) != 1.0)
|
||||
fail("json_pack real failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack real refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -73,15 +79,87 @@ static void run_tests()
|
||||
value = json_pack("s", "test");
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* nullable string (defined case) */
|
||||
value = json_pack("s?", "test");
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack nullable string (defined case) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack nullable string (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* nullable string (NULL case) */
|
||||
value = json_pack("s?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack nullable string (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack nullable string (NULL case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (int) */
|
||||
value = json_pack("s#", "test asdf", 4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length failed");
|
||||
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)))
|
||||
fail("json_pack string and length failed");
|
||||
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)))
|
||||
fail("json_pack string and length (int) failed");
|
||||
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)))
|
||||
fail("json_pack string and length (size_t) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length (size_t) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation */
|
||||
value = json_pack("s++", "te", "st", "ing");
|
||||
if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
|
||||
fail("json_pack string concatenation failed");
|
||||
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)))
|
||||
fail("json_pack string concatenation and length (int) failed");
|
||||
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)))
|
||||
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");
|
||||
json_decref(value);
|
||||
|
||||
/* empty object */
|
||||
value = json_pack("{}", 1.0);
|
||||
if(!json_is_object(value) || json_object_size(value) != 0)
|
||||
fail("json_pack empty object failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack empty object refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -89,7 +167,7 @@ static void run_tests()
|
||||
value = json_pack("[]", 1.0);
|
||||
if(!json_is_array(value) || json_array_size(value) != 0)
|
||||
fail("json_pack empty list failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack empty list failed");
|
||||
json_decref(value);
|
||||
|
||||
@@ -97,29 +175,83 @@ static void run_tests()
|
||||
value = json_pack("o", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack object failed");
|
||||
if(value->refcount != (ssize_t)1)
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack integer refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* non-incref'd nullable object (defined case) */
|
||||
value = json_pack("o?", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack nullable object (defined case) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack nullable object (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* non-incref'd nullable object (NULL case) */
|
||||
value = json_pack("o?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack nullable object (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack nullable object (NULL case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* incref'd object */
|
||||
value = json_pack("O", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack object failed");
|
||||
if(value->refcount != (ssize_t)2)
|
||||
if(value->refcount != (size_t)2)
|
||||
fail("json_pack integer refcount failed");
|
||||
json_decref(value);
|
||||
json_decref(value);
|
||||
|
||||
/* incref'd nullable object (defined case) */
|
||||
value = json_pack("O?", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack incref'd nullable object (defined case) failed");
|
||||
if(value->refcount != (size_t)2)
|
||||
fail("json_pack incref'd nullable object (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
json_decref(value);
|
||||
|
||||
/* incref'd nullable object (NULL case) */
|
||||
value = json_pack("O?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack incref'd nullable object (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack incref'd nullable object (NULL case) refcount failed");
|
||||
|
||||
/* simple object */
|
||||
value = json_pack("{s:[]}", "foo");
|
||||
if(!json_is_object(value) || json_object_size(value) != 1)
|
||||
fail("json_pack array failed");
|
||||
if(!json_is_array(json_object_get(value, "foo")))
|
||||
fail("json_pack array failed");
|
||||
if(json_object_get(value, "foo")->refcount != (ssize_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)
|
||||
fail("json_pack array failed");
|
||||
if(!json_is_array(json_object_get(value, "foobarbaz")))
|
||||
fail("json_pack array failed");
|
||||
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");
|
||||
value = json_pack("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL);
|
||||
if(!json_is_object(value) || json_object_size(value) != 0)
|
||||
fail("json_pack object optional failed");
|
||||
json_decref(value);
|
||||
|
||||
/* simple array */
|
||||
value = json_pack("[i,i,i]", 0, 1, 2);
|
||||
if(!json_is_array(value) || json_array_size(value) != 3)
|
||||
@@ -133,6 +265,18 @@ static void run_tests()
|
||||
}
|
||||
json_decref(value);
|
||||
|
||||
/* simple array with optional members */
|
||||
value = json_pack("[s,o,O]", NULL, NULL, NULL);
|
||||
if(value)
|
||||
fail("json_pack array optional incorrectly succeeded");
|
||||
value = json_pack("[**]", NULL);
|
||||
if(value)
|
||||
fail("json_pack array optional invalid incorrectly succeeded");
|
||||
value = json_pack("[s*,o*,O*]", NULL, NULL, NULL);
|
||||
if(!json_is_array(value) || json_array_size(value) != 0)
|
||||
fail("json_pack array optional failed");
|
||||
json_decref(value);
|
||||
|
||||
/* Whitespace; regular string */
|
||||
value = json_pack(" s ", "test");
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
@@ -158,73 +302,87 @@ static void run_tests()
|
||||
/* newline in format string */
|
||||
if(json_pack_ex(&error, 0, "{\n\n1"))
|
||||
fail("json_pack failed to catch invalid format '1'");
|
||||
check_error("Expected format 's', got '1'", "<format>", 3, 1, 4);
|
||||
check_error(json_error_invalid_format, "Expected format 's', got '1'", "<format>", 3, 1, 4);
|
||||
|
||||
/* mismatched open/close array/object */
|
||||
if(json_pack_ex(&error, 0, "[}"))
|
||||
fail("json_pack failed to catch mismatched '}'");
|
||||
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 2, 2);
|
||||
|
||||
if(json_pack_ex(&error, 0, "{]"))
|
||||
fail("json_pack failed to catch mismatched ']'");
|
||||
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Expected format 's', got ']'", "<format>", 1, 2, 2);
|
||||
|
||||
/* missing close array */
|
||||
if(json_pack_ex(&error, 0, "["))
|
||||
fail("json_pack failed to catch missing ']'");
|
||||
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
|
||||
/* missing close object */
|
||||
if(json_pack_ex(&error, 0, "{"))
|
||||
fail("json_pack failed to catch missing '}'");
|
||||
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
|
||||
/* garbage after format string */
|
||||
if(json_pack_ex(&error, 0, "[i]a", 42))
|
||||
fail("json_pack failed to catch garbage after format string");
|
||||
check_error("Garbage after format string", "<format>", 1, 4, 4);
|
||||
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 4, 4);
|
||||
|
||||
if(json_pack_ex(&error, 0, "ia", 42))
|
||||
fail("json_pack failed to catch garbage after format string");
|
||||
check_error("Garbage after format string", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 2, 2);
|
||||
|
||||
/* NULL string */
|
||||
if(json_pack_ex(&error, 0, "s", NULL))
|
||||
fail("json_pack failed to catch null argument string");
|
||||
check_error("NULL string argument", "<args>", 1, 1, 1);
|
||||
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 1, 1);
|
||||
|
||||
/* + on its own */
|
||||
if(json_pack_ex(&error, 0, "+", NULL))
|
||||
fail("json_pack failed to a lone +");
|
||||
check_error(json_error_invalid_format, "Unexpected format character '+'", "<format>", 1, 1, 1);
|
||||
|
||||
/* NULL format */
|
||||
if(json_pack_ex(&error, 0, NULL))
|
||||
fail("json_pack failed to catch NULL format string");
|
||||
check_error("NULL or empty format string", "<format>", -1, -1, 0);
|
||||
check_error(json_error_invalid_argument, "NULL or empty format string", "<format>", -1, -1, 0);
|
||||
|
||||
/* NULL key */
|
||||
if(json_pack_ex(&error, 0, "{s:i}", NULL, 1))
|
||||
fail("json_pack failed to catch NULL key");
|
||||
check_error("NULL object key", "<args>", 1, 2, 2);
|
||||
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 2, 2);
|
||||
|
||||
/* NULL value followed by object still steals the object's ref */
|
||||
value = json_incref(json_object());
|
||||
if(json_pack_ex(&error, 0, "{s:s,s:o}", "badnull", NULL, "dontleak", value))
|
||||
fail("json_pack failed to catch NULL value");
|
||||
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 4, 4);
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack failed to steal reference after error.");
|
||||
json_decref(value);
|
||||
|
||||
/* More complicated checks for row/columns */
|
||||
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
|
||||
fail("json_pack failed to catch object as key");
|
||||
check_error("Expected format 's', got '{'", "<format>", 1, 3, 3);
|
||||
check_error(json_error_invalid_format, "Expected format 's', got '{'", "<format>", 1, 3, 3);
|
||||
|
||||
/* Complex object */
|
||||
if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13))
|
||||
fail("json_pack failed to catch missing ]");
|
||||
check_error("Unexpected format character '}'", "<format>", 1, 19, 19);
|
||||
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 19, 19);
|
||||
|
||||
/* Complex array */
|
||||
if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"))
|
||||
fail("json_pack failed to catch extra }");
|
||||
check_error("Unexpected format character '}'", "<format>", 1, 21, 21);
|
||||
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 21, 21);
|
||||
|
||||
/* Invalid UTF-8 in object key */
|
||||
if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42))
|
||||
fail("json_pack failed to catch invalid UTF-8 in an object key");
|
||||
check_error("Invalid UTF-8 in object key", "<args>", 1, 2, 2);
|
||||
check_error(json_error_invalid_utf8, "Invalid UTF-8 object key", "<args>", 1, 2, 2);
|
||||
|
||||
/* Invalid UTF-8 in a string */
|
||||
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
|
||||
fail("json_pack failed to catch invalid UTF-8 in a string");
|
||||
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
|
||||
check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "<args>", 1, 4, 4);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -14,6 +14,24 @@ static void run_tests()
|
||||
{
|
||||
json_t *value;
|
||||
|
||||
value = json_boolean(1);
|
||||
if(!json_is_true(value))
|
||||
fail("json_boolean(1) failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_boolean(-123);
|
||||
if(!json_is_true(value))
|
||||
fail("json_boolean(-123) failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_boolean(0);
|
||||
if(!json_is_false(value))
|
||||
fail("json_boolean(0) failed");
|
||||
if(json_boolean_value(value) != 0)
|
||||
fail("json_boolean_value failed");
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_integer(1);
|
||||
if(json_typeof(value) != JSON_INTEGER)
|
||||
fail("json_typeof failed");
|
||||
@@ -56,11 +74,22 @@ static void run_tests()
|
||||
fail("json_string failed");
|
||||
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, "bar"))
|
||||
if(json_string_set(value, "barr"))
|
||||
fail("json_string_set failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
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))
|
||||
fail("json_string_set failed");
|
||||
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 5)
|
||||
fail("invalid string length");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
@@ -78,11 +107,22 @@ static void run_tests()
|
||||
fail("json_string_nocheck failed");
|
||||
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, "bar"))
|
||||
if(json_string_set_nocheck(value, "barr"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
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))
|
||||
fail("json_string_set failed");
|
||||
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 5)
|
||||
fail("invalid string length");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
@@ -92,11 +132,15 @@ static void run_tests()
|
||||
fail("json_string_nocheck failed");
|
||||
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"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
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);
|
||||
|
||||
@@ -180,4 +224,17 @@ static void run_tests()
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
|
||||
#ifdef json_auto_t
|
||||
value = json_string("foo");
|
||||
{
|
||||
json_auto_t *test = json_incref(value);
|
||||
/* Use test so GCC doesn't complain it is unused. */
|
||||
if(!json_is_string(test))
|
||||
fail("value type check failed");
|
||||
}
|
||||
if(value->refcount != 1)
|
||||
fail("automatic decrement failed");
|
||||
json_decref(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
22
test/suites/api/test_sprintf.c
Normal file
22
test/suites/api/test_sprintf.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
|
||||
static void test_sprintf() {
|
||||
json_t *s = json_sprintf("foo bar %d", 42);
|
||||
if (!s)
|
||||
fail("json_sprintf returned NULL");
|
||||
if (!json_is_string(s))
|
||||
fail("json_sprintf didn't return a JSON string");
|
||||
if (strcmp(json_string_value(s), "foo bar 42"))
|
||||
fail("json_sprintf generated an unexpected string");
|
||||
|
||||
json_decref(s);
|
||||
}
|
||||
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
test_sprintf();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
@@ -17,6 +17,7 @@ static void run_tests()
|
||||
int i1, i2, i3;
|
||||
json_int_t I1;
|
||||
int rv;
|
||||
size_t z;
|
||||
double f;
|
||||
char *s;
|
||||
|
||||
@@ -81,6 +82,13 @@ static void run_tests()
|
||||
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)
|
||||
fail("json_unpack string with length (size_t) failed");
|
||||
json_decref(j);
|
||||
|
||||
/* empty object */
|
||||
j = json_object();
|
||||
if(json_unpack(j, "{}"))
|
||||
@@ -96,14 +104,14 @@ static void run_tests()
|
||||
/* non-incref'd object */
|
||||
j = json_object();
|
||||
rv = json_unpack(j, "o", &j2);
|
||||
if(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(j2 != j || j->refcount != 2)
|
||||
if(rv || j2 != j || j->refcount != 2)
|
||||
fail("json_unpack object failed");
|
||||
json_decref(j);
|
||||
json_decref(j);
|
||||
@@ -136,65 +144,65 @@ static void run_tests()
|
||||
j = json_integer(42);
|
||||
if(!json_unpack_ex(j, &error, 0, "z"))
|
||||
fail("json_unpack succeeded with invalid format character");
|
||||
check_error("Unexpected format character 'z'", "<format>", 1, 1, 1);
|
||||
check_error(json_error_invalid_format, "Unexpected format character 'z'", "<format>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(NULL, &error, 0, "[i]"))
|
||||
fail("json_unpack succeeded with NULL root");
|
||||
check_error("NULL root value", "<root>", -1, -1, 0);
|
||||
check_error(json_error_null_value, "NULL root value", "<root>", -1, -1, 0);
|
||||
json_decref(j);
|
||||
|
||||
/* mismatched open/close array/object */
|
||||
j = json_pack("[]");
|
||||
if(!json_unpack_ex(j, &error, 0, "[}"))
|
||||
fail("json_unpack failed to catch mismatched ']'");
|
||||
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Unexpected format character '}'", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{}");
|
||||
if(!json_unpack_ex(j, &error, 0, "{]"))
|
||||
fail("json_unpack failed to catch mismatched '}'");
|
||||
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Expected format 's', got ']'", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* missing close array */
|
||||
j = json_pack("[]");
|
||||
if(!json_unpack_ex(j, &error, 0, "["))
|
||||
fail("json_unpack failed to catch missing ']'");
|
||||
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* missing close object */
|
||||
j = json_pack("{}");
|
||||
if(!json_unpack_ex(j, &error, 0, "{"))
|
||||
fail("json_unpack failed to catch missing '}'");
|
||||
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* garbage after format string */
|
||||
j = json_pack("[i]", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "[i]a", &i1))
|
||||
fail("json_unpack failed to catch garbage after format string");
|
||||
check_error("Garbage after format string", "<format>", 1, 4, 4);
|
||||
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 4, 4);
|
||||
json_decref(j);
|
||||
|
||||
j = json_integer(12345);
|
||||
if(!json_unpack_ex(j, &error, 0, "ia", &i1))
|
||||
fail("json_unpack failed to catch garbage after format string");
|
||||
check_error("Garbage after format string", "<format>", 1, 2, 2);
|
||||
check_error(json_error_invalid_format, "Garbage after format string", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* NULL format string */
|
||||
j = json_pack("[]");
|
||||
if(!json_unpack_ex(j, &error, 0, NULL))
|
||||
fail("json_unpack failed to catch null format string");
|
||||
check_error("NULL or empty format string", "<format>", -1, -1, 0);
|
||||
check_error(json_error_invalid_argument, "NULL or empty format string", "<format>", -1, -1, 0);
|
||||
json_decref(j);
|
||||
|
||||
/* NULL string pointer */
|
||||
j = json_string("foobie");
|
||||
if(!json_unpack_ex(j, &error, 0, "s", NULL))
|
||||
fail("json_unpack failed to catch null string pointer");
|
||||
check_error("NULL string argument", "<args>", 1, 1, 1);
|
||||
check_error(json_error_null_value, "NULL string argument", "<args>", 1, 1, 1);
|
||||
json_decref(j);
|
||||
|
||||
/* invalid types */
|
||||
@@ -202,39 +210,39 @@ static void run_tests()
|
||||
j2 = json_string("foo");
|
||||
if(!json_unpack_ex(j, &error, 0, "s"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected string, got integer", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected string, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "n"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected null, got integer", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected null, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "b"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected true or false, got integer", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected true or false, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j2, &error, 0, "i"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected integer, got string", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j2, &error, 0, "I"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected integer, got string", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "f"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected real, got integer", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected real, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j2, &error, 0, "F"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected real or integer, got string", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected real or integer, got string", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "[i]"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected array, got integer", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected array, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "{si}", "foo"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected object, got integer", "<validation>", 1, 1, 1);
|
||||
check_error(json_error_wrong_type, "Expected object, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
json_decref(j);
|
||||
json_decref(j2);
|
||||
@@ -243,21 +251,21 @@ static void run_tests()
|
||||
j = json_pack("[i]", 1);
|
||||
if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
|
||||
fail("json_unpack failed to catch index out of array bounds");
|
||||
check_error("Array index 1 out of range", "<validation>", 1, 3, 3);
|
||||
check_error(json_error_index_out_of_range, "Array index 1 out of range", "<validation>", 1, 3, 3);
|
||||
json_decref(j);
|
||||
|
||||
/* NULL object key */
|
||||
j = json_pack("{si}", "foo", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1))
|
||||
fail("json_unpack failed to catch null string pointer");
|
||||
check_error("NULL object key", "<args>", 1, 2, 2);
|
||||
check_error(json_error_null_value, "NULL object key", "<args>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* Object key not found */
|
||||
j = json_pack("{si}", "foo", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1))
|
||||
fail("json_unpack failed to catch null string pointer");
|
||||
check_error("Object item not found: baz", "<validation>", 1, 3, 3);
|
||||
check_error(json_error_item_not_found, "Object item not found: baz", "<validation>", 1, 3, 3);
|
||||
json_decref(j);
|
||||
|
||||
/*
|
||||
@@ -273,14 +281,14 @@ static void run_tests()
|
||||
j = json_pack("[iii]", 1, 2, 3);
|
||||
if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2))
|
||||
fail("json_unpack array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
/* Like above, but with JSON_STRICT instead of '!' format */
|
||||
j = json_pack("[iii]", 1, 2, 3);
|
||||
if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2))
|
||||
fail("json_unpack array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 4, 4);
|
||||
check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", "<validation>", 1, 4, 4);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||
@@ -290,10 +298,16 @@ static void run_tests()
|
||||
json_decref(j);
|
||||
|
||||
/* Unpack the same item twice */
|
||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||
j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1);
|
||||
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
|
||||
fail("json_unpack object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
|
||||
{
|
||||
const char *possible_errors[] = {
|
||||
"2 object item(s) left unpacked: baz, quux",
|
||||
"2 object item(s) left unpacked: quux, baz"
|
||||
};
|
||||
check_errors(json_error_end_of_input_expected, possible_errors, 2, "<validation>", 1, 10, 10);
|
||||
}
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
|
||||
@@ -306,35 +320,35 @@ static void run_tests()
|
||||
j = json_pack("[ii]", 1, 2);
|
||||
if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2))
|
||||
fail("json_unpack failed to catch ! in the middle of an array");
|
||||
check_error("Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
|
||||
check_error(json_error_invalid_format, "Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2))
|
||||
fail("json_unpack failed to catch * in the middle of an array");
|
||||
check_error("Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
|
||||
check_error(json_error_invalid_format, "Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{sssi}", "foo", "bar", "baz", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1))
|
||||
fail("json_unpack failed to catch ! in the middle of an object");
|
||||
check_error("Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
|
||||
check_error(json_error_invalid_format, "Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1))
|
||||
fail("json_unpack failed to catch ! in the middle of an object");
|
||||
check_error("Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
|
||||
check_error(json_error_invalid_format, "Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
/* Error in nested object */
|
||||
j = json_pack("{s{snsn}}", "foo", "bar", "baz");
|
||||
if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar"))
|
||||
fail("json_unpack nested object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 7, 7);
|
||||
check_error(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz", "<validation>", 1, 7, 7);
|
||||
json_decref(j);
|
||||
|
||||
/* Error in nested array */
|
||||
j = json_pack("[[ii]]", 1, 2);
|
||||
if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1))
|
||||
fail("json_unpack nested array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
/* Optional values */
|
||||
@@ -370,4 +384,23 @@ static void run_tests()
|
||||
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))
|
||||
fail("json_unpack failed for optional values with strict mode");
|
||||
if(i1 != 42)
|
||||
fail("json_unpack failed to unpack");
|
||||
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(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz", "<validation>", 1, 8, 8);
|
||||
json_decref(j);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,7 +9,7 @@
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
|
||||
#define failhdr fprintf(stderr, "%s:%d: ", __FILE__, __LINE__)
|
||||
|
||||
#define fail(msg) \
|
||||
do { \
|
||||
@@ -30,11 +30,29 @@
|
||||
} while(0)
|
||||
|
||||
/* Assumes json_error_t error */
|
||||
#define check_error(text_, source_, line_, column_, position_) \
|
||||
#define check_errors(code_, texts_, num_, source_, \
|
||||
line_, column_, position_) \
|
||||
do { \
|
||||
if(strcmp(error.text, text_) != 0) { \
|
||||
int i_, found_ = 0; \
|
||||
if(json_error_code(&error) != code_) { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
|
||||
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) { \
|
||||
@@ -61,6 +79,11 @@
|
||||
} while(0)
|
||||
|
||||
|
||||
/* Assumes json_error_t error */
|
||||
#define check_error(code_, text_, source_, line_, column_, position_) \
|
||||
check_errors(code_, &text_, 1, source_, line_, column_, position_)
|
||||
|
||||
|
||||
static void run_tests();
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
JSON_COMPACT=1
|
||||
export JSON_COMPACT
|
||||
HASHSEED=1
|
||||
export JSON_COMPACT HASHSEED
|
||||
|
||||
@@ -1 +1 @@
|
||||
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
|
||||
["foo", "\u00E5 \u00E4 \u00F6", "foo \u00E5\u00E4", "\u00E5\u00E4 foo", "\u00E5 foo \u00E4", "clef g: \uD834\uDD1E"]
|
||||
@@ -1,3 +1,4 @@
|
||||
JSON_INDENT=4
|
||||
JSON_COMPACT=1
|
||||
export JSON_INDENT JSON_COMPACT
|
||||
HASHSEED=1
|
||||
export JSON_INDENT JSON_COMPACT HASHSEED
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
JSON_INDENT=4
|
||||
export JSON_INDENT
|
||||
HASHSEED=1
|
||||
export JSON_INDENT HASHSEED
|
||||
|
||||
2
test/suites/encoding-flags/object/env
Normal file
2
test/suites/encoding-flags/object/env
Normal file
@@ -0,0 +1,2 @@
|
||||
HASHSEED=1
|
||||
export HASHSEED
|
||||
2
test/suites/encoding-flags/real-precision/env
Normal file
2
test/suites/encoding-flags/real-precision/env
Normal file
@@ -0,0 +1,2 @@
|
||||
JSON_REAL_PRECISION=4
|
||||
export JSON_REAL_PRECISION
|
||||
1
test/suites/encoding-flags/real-precision/input
Normal file
1
test/suites/encoding-flags/real-precision/input
Normal file
@@ -0,0 +1 @@
|
||||
[1.23456789, 1.0, 1.0000000000000002]
|
||||
1
test/suites/encoding-flags/real-precision/output
Normal file
1
test/suites/encoding-flags/real-precision/output
Normal file
@@ -0,0 +1 @@
|
||||
[1.235, 1.0, 1.0]
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -14,7 +14,7 @@ run_test() {
|
||||
if [ -f $test_path/env ]; then
|
||||
. $test_path/env
|
||||
fi
|
||||
$json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
|
||||
$json_process --env <$test_path/input >$test_log/stdout 2>$test_log/stderr
|
||||
)
|
||||
valgrind_check $test_log/stderr || return 1
|
||||
cmp -s $test_path/output $test_log/stdout
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -10,7 +10,7 @@ is_test() {
|
||||
}
|
||||
|
||||
run_test() {
|
||||
$json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
|
||||
$json_process --env <$test_path/input >$test_log/stdout 2>$test_log/stderr
|
||||
valgrind_check $test_log/stderr || return 1
|
||||
cmp -s $test_path/error $test_log/stderr
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
1 33 33
|
||||
\u0000 is not allowed
|
||||
@@ -1 +0,0 @@
|
||||
["\u0000 (null byte not allowed)"]
|
||||
2
test/suites/invalid/invalid-unicode-escape/error
Normal file
2
test/suites/invalid/invalid-unicode-escape/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 5 5
|
||||
invalid escape near '"\uq'
|
||||
1
test/suites/invalid/invalid-unicode-escape/input
Normal file
1
test/suites/invalid/invalid-unicode-escape/input
Normal file
@@ -0,0 +1 @@
|
||||
["\uqqqq <-- invalid unicode escape"]
|
||||
2
test/suites/invalid/null-byte-in-object-key/error
Normal file
2
test/suites/invalid/null-byte-in-object-key/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 15 15
|
||||
NUL byte in object key not supported near '"foo\u0000bar"'
|
||||
1
test/suites/invalid/null-byte-in-object-key/input
Normal file
1
test/suites/invalid/null-byte-in-object-key/input
Normal file
@@ -0,0 +1 @@
|
||||
{"foo\u0000bar": 42}
|
||||
2
test/suites/invalid/null-escape-in-string/error
Normal file
2
test/suites/invalid/null-escape-in-string/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 33 33
|
||||
\u0000 is not allowed without JSON_ALLOW_NUL
|
||||
1
test/suites/invalid/null-escape-in-string/input
Normal file
1
test/suites/invalid/null-escape-in-string/input
Normal file
@@ -0,0 +1 @@
|
||||
["null escape \u0000 not allowed"]
|
||||
2
test/suites/invalid/recursion-depth/error
Normal file
2
test/suites/invalid/recursion-depth/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 2049 2049
|
||||
maximum parsing depth reached near '['
|
||||
1
test/suites/invalid/recursion-depth/input
Normal file
1
test/suites/invalid/recursion-depth/input
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user