Compare commits
266 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dc87ed5a1 | ||
|
|
50a811ca07 | ||
|
|
42016a35c8 | ||
|
|
e83ded066a | ||
|
|
492feb26ce | ||
|
|
8f80c2d838 | ||
|
|
b9c588de3d | ||
|
|
dc69aa797b | ||
|
|
316492e4d0 | ||
|
|
b951baec0d | ||
|
|
67a7bc7376 | ||
|
|
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 | ||
|
|
ff6e6ee293 | ||
|
|
8b2bfd5586 | ||
|
|
e46b912f53 | ||
|
|
1581f26a7f | ||
|
|
aabfd493d3 | ||
|
|
bd72efbd80 | ||
|
|
e8fd3e3085 | ||
|
|
873eddaf19 | ||
|
|
bd2c0c730d | ||
|
|
17a51a4bf0 | ||
|
|
09c39adc55 | ||
|
|
cbb80baf03 | ||
|
|
040bd7b0fa | ||
|
|
2637faa450 | ||
|
|
952e1d4ba9 | ||
|
|
353b5e08ba | ||
|
|
d286e7b753 | ||
|
|
3c6e36ba2d | ||
|
|
9af64480e1 | ||
|
|
4ae5736bd0 | ||
|
|
56039ed596 | ||
|
|
02b915af54 | ||
|
|
1eb274c555 | ||
|
|
6ac6f311b5 | ||
|
|
f736e705b2 | ||
|
|
54d88753a6 | ||
|
|
98a99fb2bd | ||
|
|
ac97f6f225 | ||
|
|
60fd7ab781 | ||
|
|
0d64e8ef89 | ||
|
|
a894980258 | ||
|
|
f227483846 | ||
|
|
6cb14dd337 | ||
|
|
fa268b5017 | ||
|
|
a307974731 | ||
|
|
a2381948bb | ||
|
|
5eb2c442a9 | ||
|
|
f471e63bb3 | ||
|
|
bb24697d9b | ||
|
|
c4a7bf90cf | ||
|
|
bef87fc258 | ||
|
|
68d6410da0 | ||
|
|
1e36667193 | ||
|
|
0931d938b0 | ||
|
|
0f2cdd70ff | ||
|
|
5ec101ec21 | ||
|
|
72cd84b92a | ||
|
|
6799c8e4c9 | ||
|
|
8484ea3fb2 | ||
|
|
92d9b89d59 | ||
|
|
bc98ab6a69 | ||
|
|
32cd821273 | ||
|
|
9c6cb42f17 | ||
|
|
ed06777937 | ||
|
|
6362032513 | ||
|
|
84f739036d | ||
|
|
0f358c8eaa | ||
|
|
f0d5c04734 | ||
|
|
fd56deb7dd | ||
|
|
d7ddbf3661 | ||
|
|
b6d0191e51 | ||
|
|
2a70d62251 | ||
|
|
909874b1b9 | ||
|
|
ff57dee13d | ||
|
|
e4cc77ce52 | ||
|
|
889f295958 | ||
|
|
68809cd913 | ||
|
|
910fb92267 | ||
|
|
f241e14cab | ||
|
|
7e9c293986 | ||
|
|
d43464a1ec | ||
|
|
c7079a25eb | ||
|
|
54f38d250c | ||
|
|
c7d543d36c | ||
|
|
7fab57dcef | ||
|
|
cd9757512d | ||
|
|
c0193bfb7f | ||
|
|
6e1f4bb560 | ||
|
|
1358d0bfac | ||
|
|
86d17a8dc2 | ||
|
|
3988ab2d27 | ||
|
|
f7f7bf5ab2 | ||
|
|
a76ba52f34 | ||
|
|
9febdf333c | ||
|
|
013b8b3f60 | ||
|
|
49fc708d4c | ||
|
|
92f6f0f22c | ||
|
|
e9e34f430e | ||
|
|
ab723c7fb5 | ||
|
|
636d5f60f9 | ||
|
|
c3492973e1 | ||
|
|
e20619e071 | ||
|
|
b44e2be032 | ||
|
|
76d6d700ad | ||
|
|
c96763215d | ||
|
|
4a76900bd7 | ||
|
|
056702e541 | ||
|
|
a5a43caa9a | ||
|
|
5456fc59ab | ||
|
|
af18578928 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@ stamp-h1
|
||||
*.pyc
|
||||
*.pc
|
||||
/src/jansson_config.h
|
||||
*.exe
|
||||
|
||||
5
.travis.yml
Normal file
5
.travis.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
script: autoreconf -f -i && CFLAGS=-Werror ./configure && make check
|
||||
29
Android.mk
Normal file
29
Android.mk
Normal file
@@ -0,0 +1,29 @@
|
||||
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/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
|
||||
|
||||
LOCAL_MODULE:= libjansson
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
288
CHANGES
288
CHANGES
@@ -1,3 +1,289 @@
|
||||
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
|
||||
=============
|
||||
|
||||
Released 2012-04-20
|
||||
|
||||
* Build issues:
|
||||
|
||||
- Only use ``long long`` if ``strtoll()`` is also available.
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Fix the names of library version constants in documentation. (#52)
|
||||
|
||||
- Change the tutorial to use GitHub API v3. (#65)
|
||||
|
||||
* Tests:
|
||||
|
||||
- Make some tests locale independent. (#51)
|
||||
|
||||
- Distribute the library exports test in the tarball.
|
||||
|
||||
- Make test run on shells that don't support the ``export FOO=bar``
|
||||
syntax.
|
||||
|
||||
|
||||
Version 2.3
|
||||
===========
|
||||
|
||||
Released 2012-01-27
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_unpack()` and friends: Add support for optional object keys
|
||||
with the ``{s?o}`` syntax.
|
||||
|
||||
- Add `json_object_update_existing()` and
|
||||
`json_object_update_missing()`, for updating only existing keys or
|
||||
only adding missing keys to an object. (#37)
|
||||
|
||||
- Add `json_object_foreach()` for more convenient iteration over
|
||||
objects. (#45, #46)
|
||||
|
||||
- When decoding JSON, write the number of bytes that were read from
|
||||
input to ``error.position`` also on success. This is handy with
|
||||
``JSON_DISABLE_EOF_CHECK``.
|
||||
|
||||
- Add support for decoding any JSON value, not just arrays or
|
||||
objects. The support is enabled with the new ``JSON_DECODE_ANY``
|
||||
flag. Patch by Andrea Marchesini. (#4)
|
||||
|
||||
* Bug fixes
|
||||
|
||||
- Avoid problems with object's serial number growing too big. (#40,
|
||||
#41)
|
||||
|
||||
- Decoding functions now return NULL if the first argument is NULL.
|
||||
Patch by Andrea Marchesini.
|
||||
|
||||
- Include ``jansson_config.h.win32`` in the distribution tarball.
|
||||
|
||||
- Remove ``+`` and leading zeros from exponents in the encoder.
|
||||
(#39)
|
||||
|
||||
- Make Jansson build and work on MinGW. (#39, #38)
|
||||
|
||||
* Documentation
|
||||
|
||||
- Note that the same JSON values must not be encoded in parallel by
|
||||
separate threads. (#42)
|
||||
|
||||
- Document MinGW support.
|
||||
|
||||
|
||||
Version 2.2.1
|
||||
=============
|
||||
|
||||
Released 2011-10-06
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix real number encoding and decoding under non-C locales. (#32)
|
||||
|
||||
- Fix identifier decoding under non-UTF-8 locales. (#35)
|
||||
|
||||
- `json_load_file()`: Open the input file in binary mode for maximum
|
||||
compatiblity.
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Clarify the lifecycle of the result of the ``s`` fromat of
|
||||
`json_unpack()`. (#31)
|
||||
|
||||
- Add some portability info. (#36)
|
||||
|
||||
- Little clarifications here and there.
|
||||
|
||||
* Other:
|
||||
|
||||
- Some style fixes, issues detected by static analyzers.
|
||||
|
||||
|
||||
Version 2.2
|
||||
===========
|
||||
|
||||
Released 2011-09-03
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_dump_callback()`: Pass the encoder output to a callback
|
||||
function in chunks.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- `json_string_set()`: Check that target is a string and value is
|
||||
not NULL.
|
||||
|
||||
* Other:
|
||||
|
||||
- Documentation typo fixes and clarifications.
|
||||
|
||||
|
||||
Version 2.1
|
||||
===========
|
||||
|
||||
Released 2011-06-10
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_loadb()`: Decode a string with a given size, useful if the
|
||||
string is not null terminated.
|
||||
|
||||
- Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
|
||||
value. By default, only arrays and objects can be encoded. (#19)
|
||||
|
||||
- Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
|
||||
error if any JSON object in the input contins duplicate keys. (#3)
|
||||
|
||||
- Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
|
||||
valid JSON input. This allows other data after the JSON data.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix an additional memory leak when memory allocation fails in
|
||||
`json_object_set()` and friends.
|
||||
|
||||
- Clear errno before calling `strtod()` for better portability. (#27)
|
||||
|
||||
* Building:
|
||||
|
||||
- Avoid set-but-not-used warning/error in a test. (#20)
|
||||
|
||||
* Other:
|
||||
|
||||
- Minor clarifications to documentation.
|
||||
|
||||
|
||||
Version 2.0.1
|
||||
=============
|
||||
|
||||
@@ -10,7 +296,7 @@ Released 2011-03-31
|
||||
|
||||
- Fix object key hashing in json_unpack() strict checking mode.
|
||||
|
||||
- Fix the parentheses in JANSSON_VERSION_HEX macro.
|
||||
- Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
|
||||
|
||||
- Fix `json_object_size()` return value.
|
||||
|
||||
|
||||
512
CMakeLists.txt
Normal file
512
CMakeLists.txt
Normal file
@@ -0,0 +1,512 @@
|
||||
# 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 (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( STATIC_CRT "Link the static CRT libraries" OFF )
|
||||
endif ()
|
||||
|
||||
# Set some nicer output dirs.
|
||||
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
|
||||
# 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")
|
||||
else (WIN32)
|
||||
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.6")
|
||||
|
||||
# This is what is required to match the same numbers as automake's
|
||||
set (JANSSON_VERSION "4.6.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 (STATIC_CRT)
|
||||
set(CMAKE_C_FLAGS_RELEASE "/MT")
|
||||
set(CMAKE_C_FLAGS_DEBUG "/MTd")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "-fPIC")
|
||||
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/time.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 (uint32_t UINT32_T)
|
||||
check_type_size (__uint32 __UINT32)
|
||||
check_type_size ("unsigned long" UNSIGNED_LONG_INT)
|
||||
check_type_size ("unsigned int" UNSIGNED_INT)
|
||||
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 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 (HAVE_INLINE)
|
||||
# no inline on this platform
|
||||
set (JSON_INLINE)
|
||||
endif (HAVE_INLINE)
|
||||
|
||||
# Find our snprintf
|
||||
check_function_exists (snprintf HAVE_SNPRINTF)
|
||||
check_function_exists (_snprintf HAVE__SNPRINTF)
|
||||
|
||||
if (HAVE_SNPRINTF)
|
||||
set (JSON_SNPRINTF snprintf)
|
||||
elseif (HAVE__SNPRINTF)
|
||||
set (JSON_SNPRINTF _snprintf)
|
||||
endif ()
|
||||
|
||||
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS)
|
||||
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS)
|
||||
|
||||
# Create pkg-conf file.
|
||||
# (We use the same files as ./configure does, so we
|
||||
# have to defined the same variables used there).
|
||||
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR lib)
|
||||
endif(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(VERSION ${JANSSON_DISPLAY_VERSION})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
|
||||
|
||||
# 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/)
|
||||
|
||||
|
||||
# configure the private config file
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/private_include/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 C_FILES src/*.c)
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
|
||||
add_library (jansson SHARED ${C_FILES} src/jansson.def)
|
||||
|
||||
set_target_properties (jansson PROPERTIES
|
||||
VERSION ${JANSSON_VERSION}
|
||||
SOVERSION ${JANSSON_SOVERSION})
|
||||
|
||||
else ()
|
||||
|
||||
add_library (jansson ${C_FILES})
|
||||
|
||||
endif ()
|
||||
|
||||
# LIBRARY for linux
|
||||
# RUNTIME for windows (when building shared)
|
||||
install (TARGETS jansson
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
install (FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h
|
||||
DESTINATION include)
|
||||
|
||||
install (FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
# For building Documentation (uses Sphinx)
|
||||
OPTION (BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
|
||||
if (BUILD_DOCS)
|
||||
find_package(Sphinx)
|
||||
|
||||
if (NOT SPHINX_FOUND)
|
||||
message(WARNING "Sphinx not found. Cannot generate documentation!
|
||||
Set -DBUILD_DOCS=0 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(BUILD_MAN "Create a target for building man pages." ON)
|
||||
|
||||
if (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(BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF)
|
||||
|
||||
if (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 (WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF)
|
||||
|
||||
if (NOT WITHOUT_TESTS)
|
||||
OPTION (TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
|
||||
|
||||
ENABLE_TESTING()
|
||||
|
||||
if (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
|
||||
"--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 -Werror)
|
||||
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_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)
|
||||
add_test(${test} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test})
|
||||
|
||||
if (TEST_WITH_VALGRIND)
|
||||
add_test(memcheck_${test} ${MEMCHECK_COMMAND}
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Test harness for the suites tests.
|
||||
build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin)
|
||||
|
||||
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)
|
||||
add_test(${SUITE}__${TNAME}
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process ${TESTDIR})
|
||||
if ((${SUITE} STREQUAL "valid" OR ${SUITE} STREQUAL "invalid") AND NOT EXISTS ${TESTDIR}/nostrip)
|
||||
add_test(${SUITE}__${TNAME}__strip
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process --strip ${TESTDIR})
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
endforeach ()
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
|
||||
DEPENDS json_process ${api_tests})
|
||||
endif ()
|
||||
|
||||
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-2011 Petri Lehtinen <petri@digip.org>
|
||||
Copyright (c) 2009-2013 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 win32 CMakeLists.txt cmake
|
||||
SUBDIRS = doc src test
|
||||
|
||||
# "make distcheck" builds the dvi target, so use it to check that the
|
||||
@@ -8,3 +8,8 @@ dvi:
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = jansson.pc
|
||||
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
export AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement
|
||||
endif
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
Jansson README
|
||||
==============
|
||||
|
||||
.. image:: https://travis-ci.org/akheron/jansson.png
|
||||
:alt: Build status
|
||||
:target: https://travis-ci.org/akheron/jansson
|
||||
|
||||
Jansson_ is a C library for encoding, decoding and manipulating JSON
|
||||
data. Its main features and design principles are:
|
||||
|
||||
@@ -33,8 +37,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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2013 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,11 +24,16 @@
|
||||
#ifdef __cplusplus
|
||||
#define JSON_INLINE inline
|
||||
#else
|
||||
#define JSON_INLINE
|
||||
#define JSON_INLINE inline
|
||||
#endif
|
||||
|
||||
/* If your compiler supports the `long long` type,
|
||||
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
|
||||
/* If your compiler supports the `long long` type and the strtoll()
|
||||
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_INTEGER_IS_LONG_LONG 1
|
||||
|
||||
/* If locale.h and localeconv() are available, define to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV 0
|
||||
|
||||
#endif
|
||||
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)
|
||||
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)
|
||||
49
cmake/config.h.cmake
Normal file
49
cmake/config.h.cmake
Normal file
@@ -0,0 +1,49 @@
|
||||
#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_SSIZE_T 1
|
||||
|
||||
#ifndef HAVE_SSIZE_T
|
||||
# define ssize_t @JSON_SSIZE@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_SNPRINTF 1
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
# define snprintf @JSON_SNPRINTF@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_VSNPRINTF
|
||||
|
||||
#cmakedefine USE_URANDOM 1
|
||||
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
|
||||
62
cmake/jansson_config.h.cmake
Normal file
62
cmake/jansson_config.h.cmake
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 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 */
|
||||
#define JANSSON_USING_CMAKE
|
||||
|
||||
/* 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@
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
68
configure.ac
68
configure.ac
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.0.1], [petri@digip.org])
|
||||
AC_INIT([jansson], [2.6], [petri@digip.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
@@ -14,16 +14,12 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
|
||||
# Checks for libraries.
|
||||
|
||||
# Checks for header files.
|
||||
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_LONG_LONG_INT
|
||||
case $ac_cv_type_long_long_int in
|
||||
yes) json_have_long_long=1;;
|
||||
*) json_have_long_long=0;;
|
||||
esac
|
||||
AC_SUBST([json_have_long_long])
|
||||
|
||||
AC_C_INLINE
|
||||
case $ac_cv_c_inline in
|
||||
@@ -34,6 +30,64 @@ esac
|
||||
AC_SUBST([json_inline])
|
||||
|
||||
# Checks for library functions.
|
||||
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);],
|
||||
[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])
|
||||
fi
|
||||
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);],
|
||||
[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])
|
||||
fi
|
||||
AC_MSG_RESULT([$have_atomic_builtins])
|
||||
|
||||
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
|
||||
yesyes) json_have_long_long=1;;
|
||||
*) json_have_long_long=0;;
|
||||
esac
|
||||
AC_SUBST([json_have_long_long])
|
||||
|
||||
case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
|
||||
yesyes) json_have_localeconv=1;;
|
||||
*) json_have_localeconv=0;;
|
||||
esac
|
||||
AC_SUBST([json_have_localeconv])
|
||||
|
||||
# Features
|
||||
AC_ARG_ENABLE([urandom],
|
||||
[AS_HELP_STRING([--disable-urandom],
|
||||
[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_CONFIG_FILES([
|
||||
jansson.pc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
|
||||
gettingstarted.rst github_commits.c index.rst tutorial.rst \
|
||||
upgrading.rst ext/refcounting.py
|
||||
gettingstarted.rst github_commits.c index.rst portability.rst \
|
||||
tutorial.rst upgrading.rst ext/refcounting.py
|
||||
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
|
||||
|
||||
461
doc/apiref.rst
461
doc/apiref.rst
@@ -41,7 +41,7 @@ set to zero.
|
||||
The following preprocessor constants specify the current version of
|
||||
the library:
|
||||
|
||||
``JANSSON_VERSION_MAJOR``, ``JANSSON_VERSION_MINOR``, ``JANSSON_VERSION_MICRO``
|
||||
``JANSSON_MAJOR_VERSION``, ``JANSSON_MINOR_VERSION``, ``JANSSON_MICRO_VERSION``
|
||||
Integers specifying the major, minor and micro versions,
|
||||
respectively.
|
||||
|
||||
@@ -168,7 +168,7 @@ The following functions are used to manipulate the reference count.
|
||||
|
||||
.. function:: json_t *json_incref(json_t *json)
|
||||
|
||||
Increment the reference count of *json* if it's not non-*NULL*.
|
||||
Increment the reference count of *json* if it's not *NULL*.
|
||||
Returns *json*.
|
||||
|
||||
.. function:: void json_decref(json_t *json)
|
||||
@@ -255,8 +255,8 @@ returns an error status.
|
||||
True, False and Null
|
||||
====================
|
||||
|
||||
These values are implemented as singletons, so each of these functions
|
||||
returns the same value each time.
|
||||
These three values are implemented as singletons, so the returned
|
||||
pointers won't change between invocations of these functions.
|
||||
|
||||
.. function:: json_t *json_true(void)
|
||||
|
||||
@@ -270,6 +270,17 @@ returns the same value each time.
|
||||
|
||||
Returns the JSON false value.
|
||||
|
||||
.. function:: json_t *json_boolean(val)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns JSON false if ``val`` is zero, and JSON true otherwise.
|
||||
This is a macro, and equivalent to ``val ? json_true() :
|
||||
json_false()``.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
|
||||
.. function:: json_t *json_null(void)
|
||||
|
||||
.. refcounting:: new
|
||||
@@ -346,7 +357,6 @@ information, see :ref:`rfc-conformance`.
|
||||
should use ``json_int_t`` explicitly.
|
||||
|
||||
``JSON_INTEGER_IS_LONG_LONG``
|
||||
|
||||
This is a preprocessor variable that holds the value 1 if
|
||||
:type:`json_int_t` is ``long long``, and 0 if it's ``long``. It
|
||||
can be used as follows::
|
||||
@@ -358,7 +368,6 @@ information, see :ref:`rfc-conformance`.
|
||||
#endif
|
||||
|
||||
``JSON_INTEGER_FORMAT``
|
||||
|
||||
This is a macro that expands to a :func:`printf()` conversion
|
||||
specifier that corresponds to :type:`json_int_t`, without the
|
||||
leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro
|
||||
@@ -481,18 +490,48 @@ A JSON array is an ordered collection of other JSON values.
|
||||
|
||||
Removes the element in *array* at position *index*, shifting the
|
||||
elements after *index* one position towards the start of the array.
|
||||
Returns 0 on success and -1 on error.
|
||||
Returns 0 on success and -1 on error. The reference count of the
|
||||
removed value is decremented.
|
||||
|
||||
.. function:: int json_array_clear(json_t *array)
|
||||
|
||||
Removes all elements from *array*. Returns 0 on sucess and -1 on
|
||||
error.
|
||||
error. The reference count of all removed values are decremented.
|
||||
|
||||
.. function:: int json_array_extend(json_t *array, json_t *other_array)
|
||||
|
||||
Appends all elements in *other_array* to the end of *array*.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
The following macro can be used to iterate through all elements
|
||||
in an array.
|
||||
|
||||
.. function:: json_array_foreach(array, index, value)
|
||||
|
||||
Iterate over every element of ``array``, running the block
|
||||
of code that follows each time with the proper values set to
|
||||
variables ``index`` and ``value``, of types :type:`size_t` and
|
||||
:type:`json_t *` respectively. Example::
|
||||
|
||||
/* array is a JSON array */
|
||||
size_t index;
|
||||
json_t *value;
|
||||
|
||||
json_array_foreach(array, index, value) {
|
||||
/* block of code that uses index and value */
|
||||
}
|
||||
|
||||
The items are returned in increasing index order.
|
||||
|
||||
This macro expands to an ordinary ``for`` statement upon
|
||||
preprocessing, so its performance is equivalent to that of
|
||||
hand-written code using the array access functions.
|
||||
The main advantage of this macro is that it abstracts
|
||||
away the complexity, and makes for shorter, more
|
||||
concise code.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
|
||||
Object
|
||||
======
|
||||
@@ -549,21 +588,70 @@ Unicode string and the value is any JSON value.
|
||||
.. function:: int json_object_del(json_t *object, const char *key)
|
||||
|
||||
Delete *key* from *object* if it exists. Returns 0 on success, or
|
||||
-1 if *key* was not found.
|
||||
|
||||
-1 if *key* was not found. The reference count of the removed value
|
||||
is decremented.
|
||||
|
||||
.. function:: int json_object_clear(json_t *object)
|
||||
|
||||
Remove all elements from *object*. Returns 0 on success and -1 if
|
||||
*object* is not a JSON object.
|
||||
*object* is not a JSON object. The reference count of all removed
|
||||
values are decremented.
|
||||
|
||||
.. function:: int json_object_update(json_t *object, json_t *other)
|
||||
|
||||
Update *object* with the key-value pairs from *other*, overwriting
|
||||
existing keys. Returns 0 on success or -1 on error.
|
||||
|
||||
.. function:: int json_object_update_existing(json_t *object, json_t *other)
|
||||
|
||||
The following functions implement an iteration protocol for objects:
|
||||
Like :func:`json_object_update()`, but only the values of existing
|
||||
keys are updated. No new keys are created. Returns 0 on success or
|
||||
-1 on error.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
.. function:: int json_object_update_missing(json_t *object, json_t *other)
|
||||
|
||||
Like :func:`json_object_update()`, but only new keys are created.
|
||||
The value of any existing key is not changed. Returns 0 on success
|
||||
or -1 on error.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
The following macro can be used to iterate through all key-value pairs
|
||||
in an object.
|
||||
|
||||
.. function:: json_object_foreach(object, key, value)
|
||||
|
||||
Iterate over every key-value pair of ``object``, running the block
|
||||
of code that follows each time with the proper values set to
|
||||
variables ``key`` and ``value``, of types :type:`const char *` and
|
||||
:type:`json_t *` respectively. Example::
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
json_object_foreach(obj, key, value) {
|
||||
/* block of code that uses key and value */
|
||||
}
|
||||
|
||||
The items are not returned in any particular order.
|
||||
|
||||
This macro expands to an ordinary ``for`` statement upon
|
||||
preprocessing, so its performance is equivalent to that of
|
||||
hand-written iteration code using the object iteration protocol
|
||||
(see below). The main advantage of this macro is that it abstracts
|
||||
away the complexity behind iteration, and makes for shorter, more
|
||||
concise code.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
|
||||
The following functions implement an iteration protocol for objects,
|
||||
allowing to iterate through all key-value pairs in an object. The
|
||||
items are not returned in any particular order, as this would require
|
||||
sorting due to the internal hashtable implementation.
|
||||
|
||||
.. function:: void *json_object_iter(json_t *object)
|
||||
|
||||
@@ -605,11 +693,21 @@ The following functions implement an iteration protocol for objects:
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. function:: void *json_object_key_to_iter(const char *key)
|
||||
|
||||
Like :func:`json_object_iter_at()`, but much faster. Only works for
|
||||
values returned by :func:`json_object_iter_key()`. Using other keys
|
||||
will lead to segfaults. This function is used internally to
|
||||
implement :func:`json_object_foreach`.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
The iteration protocol can be used for example as follows::
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
void *iter = json_object_iter(obj);
|
||||
while(iter)
|
||||
{
|
||||
@@ -619,6 +717,32 @@ The iteration protocol can be used for example as follows::
|
||||
iter = json_object_iter_next(obj, iter);
|
||||
}
|
||||
|
||||
.. function:: void json_object_seed(size_t seed)
|
||||
|
||||
Seed the hash function used in Jansson's hashtable implementation.
|
||||
The seed is used to randomize the hash function so that an
|
||||
attacker cannot control its output.
|
||||
|
||||
If *seed* is 0, Jansson generates the seed itselfy by reading
|
||||
random data from the operating system's entropy sources. If no
|
||||
entropy sources are available, falls back to using a combination
|
||||
of the current timestamp (with microsecond precision if possible)
|
||||
and the process ID.
|
||||
|
||||
If called at all, this function must be called before any calls to
|
||||
:func:`json_object()`, either explicit or implicit. If this
|
||||
function is not called by the user, the first call to
|
||||
:func:`json_object()` (either explicit or implicit) seeds the hash
|
||||
function. See :ref:`portability-thread-safety` for notes on thread
|
||||
safety.
|
||||
|
||||
If repeatable results are required, for e.g. unit tests, the hash
|
||||
function can be "unrandomized" by calling :func:`json_object_seed`
|
||||
with a constant value on program startup, e.g.
|
||||
``json_object_seed(1)``.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
|
||||
Error reporting
|
||||
===============
|
||||
@@ -670,7 +794,9 @@ and pass a pointer to a function. Example::
|
||||
}
|
||||
|
||||
Also note that if the call succeeded (``json != NULL`` in the above
|
||||
example), the contents of ``error`` are unspecified.
|
||||
example), the contents of ``error`` are generally left unspecified.
|
||||
The decoding functions write to the ``position`` member also on
|
||||
success. See :ref:`apiref-decoding` for more info.
|
||||
|
||||
All functions also accept *NULL* as the :type:`json_error_t` pointer,
|
||||
in which case no error information is returned to the caller.
|
||||
@@ -680,8 +806,10 @@ Encoding
|
||||
========
|
||||
|
||||
This sections describes the functions that can be used to encode
|
||||
values to JSON. Only objects and arrays can be encoded, since they are
|
||||
the only valid "root" values of a JSON text.
|
||||
values to JSON. By default, only objects and arrays can be encoded
|
||||
directly, since they are the only valid *root* values of a JSON text.
|
||||
To encode any JSON value, use the ``JSON_ENCODE_ANY`` flag (see
|
||||
below).
|
||||
|
||||
By default, the output has no newlines, and spaces are used between
|
||||
array and object elements for a readable output. This behavior can be
|
||||
@@ -696,9 +824,9 @@ can be ORed together to obtain *flags*.
|
||||
``JSON_INDENT(n)``
|
||||
Pretty-print the result, using newlines between array and object
|
||||
items, and indenting with *n* spaces. The valid range for *n* is
|
||||
between 0 and 32, other values result in an undefined output. If
|
||||
``JSON_INDENT`` is not used or *n* is 0, no newlines are inserted
|
||||
between array and object items.
|
||||
between 0 and 31 (inclusive), other values result in an undefined
|
||||
output. If ``JSON_INDENT`` is not used or *n* is 0, no newlines are
|
||||
inserted between array and object items.
|
||||
|
||||
``JSON_COMPACT``
|
||||
This flag enables a compact representation, i.e. sets the separator
|
||||
@@ -722,6 +850,23 @@ can be ORed together to obtain *flags*.
|
||||
example, decoding a JSON text and then encoding with this flag
|
||||
preserves the order of object keys.
|
||||
|
||||
``JSON_ENCODE_ANY``
|
||||
Specifying this flag makes it possible to encode any JSON value on
|
||||
its own. Without it, only objects and arrays can be passed as the
|
||||
*root* value to the encoding functions.
|
||||
|
||||
**Note:** Encoding any value may be useful in some scenarios, but
|
||||
it's generally discouraged as it violates strict compatiblity with
|
||||
:rfc:`4627`. If you use this flag, don't expect interoperatibility
|
||||
with other JSON systems.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
``JSON_ESCAPE_SLASH``
|
||||
Escape the ``/`` characters in strings with ``\/``.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
The following functions perform the actual JSON encoding. The result
|
||||
is in UTF-8.
|
||||
|
||||
@@ -745,6 +890,30 @@ is in UTF-8.
|
||||
*path* already exists, it is overwritten. *flags* is described
|
||||
above. Returns 0 on success and -1 on error.
|
||||
|
||||
.. type:: json_dump_callback_t
|
||||
|
||||
A typedef for a function that's called by
|
||||
:func:`json_dump_callback()`::
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
*buffer* points to a buffer containing a chunk of output, *size* is
|
||||
the length of the buffer, and *data* is the corresponding
|
||||
:func:`json_dump_callback()` argument passed through.
|
||||
|
||||
On error, the function should return -1 to stop the encoding
|
||||
process. On success, it should return 0.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
||||
|
||||
Call *callback* repeatedly, passing a chunk of the JSON
|
||||
representation of *root* each time. *flags* is described above.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
|
||||
.. _apiref-decoding:
|
||||
|
||||
@@ -756,20 +925,98 @@ text to the Jansson representation of JSON data. The JSON
|
||||
specification requires that a JSON text is either a serialized array
|
||||
or object, and this requirement is also enforced with the following
|
||||
functions. In other words, the top level value in the JSON text being
|
||||
decoded must be either array or object.
|
||||
decoded must be either array or object. To decode any JSON value, use
|
||||
the ``JSON_DECODE_ANY`` flag (see below).
|
||||
|
||||
See :ref:`rfc-conformance` for a discussion on Jansson's conformance
|
||||
to the JSON specification. It explains many design decisions that
|
||||
affect especially the behavior of the decoder.
|
||||
|
||||
Each function takes a *flags* parameter that can be used to control
|
||||
the behavior of the decoder. Its default value is 0. The following
|
||||
macros can be ORed together to obtain *flags*.
|
||||
|
||||
``JSON_REJECT_DUPLICATES``
|
||||
Issue a decoding error if any JSON object in the input text
|
||||
contains duplicate keys. Without this flag, the value of the last
|
||||
occurence of each key ends up in the result. Key equivalence is
|
||||
checked byte-by-byte, without special Unicode comparison
|
||||
algorithms.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
``JSON_DECODE_ANY``
|
||||
By default, the decoder expects an array or object as the input.
|
||||
With this flag enabled, the decoder accepts any valid JSON value.
|
||||
|
||||
**Note:** Decoding any value may be useful in some scenarios, but
|
||||
it's generally discouraged as it violates strict compatiblity with
|
||||
:rfc:`4627`. If you use this flag, don't expect interoperatibility
|
||||
with other JSON systems.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
``JSON_DISABLE_EOF_CHECK``
|
||||
By default, the decoder expects that its whole input constitutes a
|
||||
valid JSON text, and issues an error if there's extra data after
|
||||
the otherwise valid JSON input. With this flag enabled, the decoder
|
||||
stops after decoding a valid JSON array or object, and thus allows
|
||||
extra data after the JSON text.
|
||||
|
||||
Normally, reading will stop when the last ``]`` or ``}`` in the
|
||||
JSON input is encountered. If both ``JSON_DISABLE_EOF_CHECK`` and
|
||||
``JSON_DECODE_ANY`` flags are used, the decoder may read one extra
|
||||
UTF-8 code unit (up to 4 bytes of input). For example, decoding
|
||||
``4true`` correctly decodes the integer 4, but also reads the
|
||||
``t``. For this reason, if reading multiple consecutive values that
|
||||
are not arrays or objects, they should be separated by at least one
|
||||
whitespace character.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
``JSON_DECODE_INT_AS_REAL``
|
||||
JSON defines only one number type. Jansson distinguishes between
|
||||
ints and reals. For more information see :ref:`real-vs-integer`.
|
||||
With this flag enabled the decoder interprets all numbers as real
|
||||
values. Integers that do not have an exact double representation
|
||||
will silently result in a loss of precision. Integers that cause
|
||||
a double overflow will cause an error.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
Each function also takes an optional :type:`json_error_t` parameter
|
||||
that is filled with error information if decoding fails. It's also
|
||||
updated on success; the number of bytes of input read is written to
|
||||
its ``position`` field. This is especially useful when using
|
||||
``JSON_DISABLE_EOF_CHECK`` to read multiple consecutive JSON texts.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
Number of bytes of input read is written to the ``position`` field
|
||||
of the :type:`json_error_t` structure.
|
||||
|
||||
If no error or position information is needed, you can pass *NULL*.
|
||||
|
||||
The following functions perform the actual JSON decoding.
|
||||
|
||||
.. function:: json_t *json_loads(const char *input, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *input* and returns the array or object it
|
||||
contains, or *NULL* on error, in which case *error* is filled with
|
||||
information about the error. *flags* is currently unused, and
|
||||
should be set to 0.
|
||||
information about the error. *flags* is described above.
|
||||
|
||||
.. function:: json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *buffer*, whose length is *buflen*, and
|
||||
returns the array or object it contains, or *NULL* on error, in
|
||||
which case *error* is filled with information about the error. This
|
||||
is similar to :func:`json_loads()` except that the string doesn't
|
||||
need to be null-terminated. *flags* is described above.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. function:: json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
|
||||
@@ -777,8 +1024,18 @@ affect especially the behavior of the decoder.
|
||||
|
||||
Decodes the JSON text in stream *input* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
This function will start reading the input from whatever position
|
||||
the input file was, without attempting to seek first. If an error
|
||||
occurs, the file position will be left indeterminate. On success,
|
||||
the file position will be at EOF, unless ``JSON_DISABLE_EOF_CHECK``
|
||||
flag was used. In this case, the file position will be at the first
|
||||
character after the last ``]`` or ``}`` in the JSON input. This
|
||||
allows calling :func:`json_loadf()` on the same ``FILE`` object
|
||||
multiple times, if the input consists of consecutive JSON texts,
|
||||
possibly separated by whitespace.
|
||||
|
||||
.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
|
||||
@@ -786,8 +1043,37 @@ affect especially the behavior of the decoder.
|
||||
|
||||
Decodes the JSON text in file *path* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
.. type:: json_load_callback_t
|
||||
|
||||
A typedef for a function that's called by
|
||||
:func:`json_load_callback()` to read a chunk of input data::
|
||||
|
||||
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
|
||||
|
||||
*buffer* points to a buffer of *buflen* bytes, and *data* is the
|
||||
corresponding :func:`json_load_callback()` argument passed through.
|
||||
|
||||
On success, the function should return the number of bytes read; a
|
||||
returned value of 0 indicates that no data was read and that the
|
||||
end of file has been reached. On error, the function should return
|
||||
``(size_t)-1`` to abort the decoding process.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
.. function:: json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON text produced by repeated calls to *callback*, and
|
||||
returns the array or object it contains, or *NULL* on error, in
|
||||
which case *error* is filled with information about the error.
|
||||
*data* is passed through to *callback* on each call. *flags* is
|
||||
described above.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
|
||||
.. _apiref-pack:
|
||||
@@ -795,14 +1081,14 @@ affect especially the behavior of the decoder.
|
||||
Building Values
|
||||
===============
|
||||
|
||||
This sectinon describes functions that help to create, or *pack*,
|
||||
This section describes functions that help to create, or *pack*,
|
||||
complex JSON values, especially nested objects and arrays. Value
|
||||
building is based on a *format string* that is used to tell the
|
||||
functions about the expected arguments.
|
||||
|
||||
For example, the format string ``"i"`` specifies a single integer
|
||||
value, while the format string ``"[ssb]"`` or the equivalent ``"[s, s,
|
||||
b]"`` specifies an array value with two integers and a boolean as its
|
||||
b]"`` specifies an array value with two strings and a boolean as its
|
||||
items::
|
||||
|
||||
/* Create the JSON integer 42 */
|
||||
@@ -811,13 +1097,31 @@ items::
|
||||
/* Create the JSON array ["foo", "bar", true] */
|
||||
json_pack("[ssb]", "foo", "bar", 1);
|
||||
|
||||
Here's the full list of format characters. The type in parentheses
|
||||
Here's the full list of format specifiers. The type in parentheses
|
||||
denotes the resulting JSON type, and the type in brackets (if any)
|
||||
denotes the C type that is expected as the corresponding argument.
|
||||
denotes the C type that is expected as the corresponding argument or
|
||||
arguments.
|
||||
|
||||
``s`` (string) [const char \*]
|
||||
Convert a NULL terminated UTF-8 string to a JSON string.
|
||||
|
||||
``s#`` (string) [const char \*, int]
|
||||
Convert a UTF-8 buffer of a given length to a JSON string.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
``+`` [const char \*]
|
||||
Like ``s``, but concatenate to the previous string. Only valid
|
||||
after ``s``, ``s#``, ``+`` or ``+#``.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
``+#`` [const char \*, int]
|
||||
Like ``s#``, but concatenate to the previous string. Only valid
|
||||
after ``s``, ``s#``, ``+`` or ``+#``.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
``n`` (null)
|
||||
Output a JSON null value. No argument is consumed.
|
||||
|
||||
@@ -837,7 +1141,7 @@ denotes the C type that is expected as the corresponding argument.
|
||||
``o`` (any value) [json_t \*]
|
||||
Output any given JSON value as-is. If the value is added to an
|
||||
array or object, the reference to the value passed to ``o`` is
|
||||
stealed by the container.
|
||||
stolen by the container.
|
||||
|
||||
``O`` (any value) [json_t \*]
|
||||
Like ``o``, but the argument's reference count is incremented.
|
||||
@@ -852,10 +1156,13 @@ denotes the C type that is expected as the corresponding argument.
|
||||
|
||||
``{fmt}`` (object)
|
||||
Build an object with contents from the inner format string
|
||||
``fmt``. The first, third, etc. format character represent a key,
|
||||
and must be ``s`` (as object keys are always strings). The second,
|
||||
fourth, etc. format character represent a value. Any value may be
|
||||
an object or array, i.e. recursive value building is supported.
|
||||
``fmt``. The first, third, etc. format specifier represent a key,
|
||||
and must be a string (see ``s``, ``s#``, ``+`` and ``+#`` above),
|
||||
as object keys are always strings. The second, fourth, etc. format
|
||||
specifier represent a value. Any value may be an object or array,
|
||||
i.e. recursive value building is supported.
|
||||
|
||||
Whitespace, ``:`` and ``,`` are ignored.
|
||||
|
||||
The following functions compose the value building API:
|
||||
|
||||
@@ -864,9 +1171,9 @@ The following functions compose the value building API:
|
||||
.. refcounting:: new
|
||||
|
||||
Build a new JSON value according to the format string *fmt*. For
|
||||
each format character (except for ``{}[]n``), one argument is
|
||||
consumed and used to build the corresponding value. Returns *NULL*
|
||||
on error.
|
||||
each format specifier (except for ``{}[]n``), one or more arguments
|
||||
are consumed and used to build the corresponding value. Returns
|
||||
*NULL* on error.
|
||||
|
||||
.. function:: json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
|
||||
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap)
|
||||
@@ -887,13 +1194,20 @@ More examples::
|
||||
json_pack("{}");
|
||||
|
||||
/* Build the JSON object {"foo": 42, "bar": 7} */
|
||||
json_pack("{sisb}", "foo", 42, "bar", 7);
|
||||
json_pack("{sisi}", "foo", 42, "bar", 7);
|
||||
|
||||
/* Like above, ':', ',' and whitespace are ignored */
|
||||
json_pack("{s:i, s:b}", "foo", 42, "bar", 7);
|
||||
json_pack("{s:i, s:i}", "foo", 42, "bar", 7);
|
||||
|
||||
/* Build the JSON array [[1, 2], {"cool": true}] */
|
||||
json_pack("[[i,i],{s:b]]", 1, 2, "cool", 1);
|
||||
json_pack("[[i,i],{s:b}]", 1, 2, "cool", 1);
|
||||
|
||||
/* Build a string from a non-NUL terminated buffer */
|
||||
char buffer[4] = {'t', 'e', 's', 't'};
|
||||
json_pack("s#", buffer, 4);
|
||||
|
||||
/* Concatentate strings together to build the JSON string "foobarbaz" */
|
||||
json_pack("s++", "foo", "bar", "baz");
|
||||
|
||||
|
||||
.. _apiref-unpack:
|
||||
@@ -901,7 +1215,7 @@ More examples::
|
||||
Parsing and Validating Values
|
||||
=============================
|
||||
|
||||
This sectinon describes functions that help to validate complex values
|
||||
This section describes functions that help to validate complex values
|
||||
and extract, or *unpack*, data from them. Like :ref:`building values
|
||||
<apiref-pack>`, this is also based on format strings.
|
||||
|
||||
@@ -909,16 +1223,18 @@ While a JSON value is unpacked, the type specified in the format
|
||||
string is checked to match that of the JSON value. This is the
|
||||
validation part of the process. In addition to this, the unpacking
|
||||
functions can also check that all items of arrays and objects are
|
||||
unpacked. This check be enabled with the format character ``!`` or by
|
||||
unpacked. This check be enabled with the format specifier ``!`` or by
|
||||
using the flag ``JSON_STRICT``. See below for details.
|
||||
|
||||
Here's the full list of format characters. The type in parentheses
|
||||
Here's the full list of format specifiers. The type in parentheses
|
||||
denotes the JSON type, and the type in brackets (if any) denotes the C
|
||||
type whose address should be passed.
|
||||
|
||||
``s`` (string) [const char \*]
|
||||
Convert a JSON string to a pointer to a NULL terminated UTF-8
|
||||
string.
|
||||
string. The resulting string is extracted by using
|
||||
:func:`json_string_value()` internally, so it exists as long as
|
||||
there are still references to the corresponding JSON string.
|
||||
|
||||
``n`` (null)
|
||||
Expect a JSON null value. Nothing is extracted.
|
||||
@@ -952,30 +1268,37 @@ type whose address should be passed.
|
||||
|
||||
``{fmt}`` (object)
|
||||
Convert each item in the JSON object according to the inner format
|
||||
string ``fmt``. The first, third, etc. format character represent
|
||||
string ``fmt``. The first, third, etc. format specifier represent
|
||||
a key, and must be ``s``. The corresponding argument to unpack
|
||||
functions is read as the object key. The second fourth, etc.
|
||||
format character represent a value and is written to the address
|
||||
format specifier represent a value and is written to the address
|
||||
given as the corresponding argument. **Note** that every other
|
||||
argument is read from and every other is written to.
|
||||
|
||||
``fmt`` may contain objects and arrays as values, i.e. recursive
|
||||
value extraction is supporetd.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
Any ``s`` representing a key may be suffixed with a ``?`` to
|
||||
make the key optional. If the key is not found, nothing is
|
||||
extracted. See below for an example.
|
||||
|
||||
``!``
|
||||
This special format character is used to enable the check that
|
||||
This special format specifier is used to enable the check that
|
||||
all object and array items are accessed, on a per-value basis. It
|
||||
must appear inside an array or object as the last format character
|
||||
must appear inside an array or object as the last format specifier
|
||||
before the closing bracket or brace. To enable the check globally,
|
||||
use the ``JSON_STRICT`` unpacking flag.
|
||||
|
||||
``*``
|
||||
This special format character is the opposite of ``!``. If the
|
||||
This special format specifier is the opposite of ``!``. If the
|
||||
``JSON_STRICT`` flag is used, ``*`` can be used to disable the
|
||||
strict check on a per-value basis. It must appear inside an array
|
||||
or object as the last format character before the closing bracket
|
||||
or object as the last format specifier before the closing bracket
|
||||
or brace.
|
||||
|
||||
Whitespace, ``:`` and ``,`` are ignored.
|
||||
|
||||
The following functions compose the parsing and validation API:
|
||||
|
||||
.. function:: int json_unpack(json_t *root, const char *fmt, ...)
|
||||
@@ -992,12 +1315,26 @@ The following functions compose the parsing and validation API:
|
||||
behaviour of the unpacker, see below for the flags. Returns 0 on
|
||||
success and -1 on failure.
|
||||
|
||||
.. note::
|
||||
|
||||
The first argument of all unpack functions is ``json_t *root``
|
||||
instead of ``const json_t *root``, because the use of ``O`` format
|
||||
specifier causes the reference count of ``root``, or some value
|
||||
reachable from ``root``, to be increased. Furthermore, the ``o``
|
||||
format specifier may be used to extract a value as-is, which allows
|
||||
modifying the structure or contents of a value reachable from
|
||||
``root``.
|
||||
|
||||
If the ``O`` and ``o`` format specifiers are not used, it's
|
||||
perfectly safe to cast a ``const json_t *`` variable to plain
|
||||
``json_t *`` when used with these functions.
|
||||
|
||||
The following unpacking flags are available:
|
||||
|
||||
``JSON_STRICT``
|
||||
Enable the extra validation step checking that all object and
|
||||
array items are unpacked. This is equivalent to appending the
|
||||
format character ``!`` to the end of every array and object in the
|
||||
format specifier ``!`` to the end of every array and object in the
|
||||
format string.
|
||||
|
||||
``JSON_VALIDATE_ONLY``
|
||||
@@ -1028,6 +1365,13 @@ Examples::
|
||||
json_unpack(root, "[ii!]", &myint1, &myint2);
|
||||
/* returns -1 for failed validation */
|
||||
|
||||
/* root is an empty JSON object */
|
||||
int myint = 0, myint2 = 0;
|
||||
json_unpack(root, "{s?i, s?[ii]}",
|
||||
"foo", &myint1,
|
||||
"bar", &myint2, &myint3);
|
||||
/* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */
|
||||
|
||||
|
||||
Equality
|
||||
========
|
||||
@@ -1089,13 +1433,15 @@ copied in a recursive fashion.
|
||||
|
||||
Returns a shallow copy of *value*, or *NULL* on error.
|
||||
|
||||
.. function:: json_t *json_deep_copy(json_t *value)
|
||||
.. function:: json_t *json_deep_copy(const json_t *value)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a deep copy of *value*, or *NULL* on error.
|
||||
|
||||
|
||||
.. _apiref-custom-memory-allocation:
|
||||
|
||||
Custom Memory Allocation
|
||||
========================
|
||||
|
||||
@@ -1124,7 +1470,12 @@ behavior is needed.
|
||||
Jansson's API functions to ensure that all memory operations use
|
||||
the same functions.
|
||||
|
||||
Examples:
|
||||
**Examples:**
|
||||
|
||||
Circumvent problems with different CRT heaps on Windows by using
|
||||
application's :func:`malloc()` and :func:`free()`::
|
||||
|
||||
json_set_alloc_funcs(malloc, free);
|
||||
|
||||
Use the `Boehm's conservative garbage collector`_ for memory
|
||||
operations::
|
||||
@@ -1151,7 +1502,7 @@ JSON structures by zeroing all memory when freed::
|
||||
ptr -= 8;
|
||||
size = *((size_t *)ptr);
|
||||
|
||||
guaranteed_memset(ptr, 0, size);
|
||||
guaranteed_memset(ptr, 0, size + 8);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
@@ -1164,5 +1515,5 @@ JSON structures by zeroing all memory when freed::
|
||||
For more information about the issues of storing sensitive data in
|
||||
memory, see
|
||||
http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html.
|
||||
The page also examplains the :func:`guaranteed_memset()` function used
|
||||
The page also explains the :func:`guaranteed_memset()` function used
|
||||
in the example and gives a sample implementation for it.
|
||||
|
||||
@@ -41,16 +41,16 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Jansson'
|
||||
copyright = u'2009-2011, Petri Lehtinen'
|
||||
copyright = u'2009-2013, 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.0'
|
||||
version = '2.6'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.0.1'
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -5,8 +5,7 @@ RFC Conformance
|
||||
***************
|
||||
|
||||
JSON is specified in :rfc:`4627`, *"The application/json Media Type
|
||||
for JavaScript Object Notation (JSON)"*. This chapter discusses
|
||||
Jansson's conformance to this specification.
|
||||
for JavaScript Object Notation (JSON)"*.
|
||||
|
||||
Character Encoding
|
||||
==================
|
||||
@@ -30,9 +29,17 @@ error::
|
||||
|
||||
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
equivalence of strings or object keys, the comparison is performed
|
||||
byte by byte between the original UTF-8 representations of the
|
||||
strings.
|
||||
|
||||
Numbers
|
||||
=======
|
||||
|
||||
.. _real-vs-integer:
|
||||
|
||||
Real vs. Integer
|
||||
----------------
|
||||
|
||||
@@ -46,7 +53,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.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<description of the json_object function>
|
||||
|
||||
:copyright: Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
:copyright: Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
:license: MIT, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ Compiling and Installing Jansson
|
||||
The Jansson source is available at
|
||||
http://www.digip.org/jansson/releases/.
|
||||
|
||||
Unix-like systems
|
||||
-----------------
|
||||
Unix-like systems (including MinGW)
|
||||
-----------------------------------
|
||||
|
||||
Unpack the source tarball and change to the source directory:
|
||||
|
||||
@@ -54,22 +54,136 @@ 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 -DBUILD_SHARED=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.
|
||||
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
**This method is deprecated**. Using :ref:`CMake <build-cmake>` is now
|
||||
preferred.
|
||||
|
||||
Jansson can be built with Visual Studio 2010 (and probably newer
|
||||
versions, too). The solution and project files are in the
|
||||
``win32/vs2010/`` 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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.
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
|
||||
#define URL_SIZE 256
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
@@ -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[])
|
||||
@@ -102,7 +123,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
@@ -126,42 +146,52 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
if(!json_is_array(root))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
fprintf(stderr, "error: root is not an array\n");
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
for(i = 0; i < json_array_size(root); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
json_t *data, *sha, *commit, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
data = json_array_get(root, i);
|
||||
if(!json_is_object(data))
|
||||
{
|
||||
fprintf(stderr, "error: commit %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;
|
||||
}
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
sha = json_object_get(data, "sha");
|
||||
if(!json_is_string(sha))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id 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", (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;
|
||||
}
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
json_string_value(sha),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ data. Its main features and design principles are:
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
Jansson is used in production and its API is stable. It works on
|
||||
numerous platforms, including numerous Unix like systems and Windows.
|
||||
It's suitable for use on any system, including desktop, server, and
|
||||
small embedded systems.
|
||||
|
||||
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
@@ -36,6 +41,7 @@ Contents
|
||||
upgrading
|
||||
tutorial
|
||||
conformance
|
||||
portability
|
||||
apiref
|
||||
changes
|
||||
|
||||
|
||||
83
doc/portability.rst
Normal file
83
doc/portability.rst
Normal file
@@ -0,0 +1,83 @@
|
||||
***********
|
||||
Portability
|
||||
***********
|
||||
|
||||
.. _portability-thread-safety:
|
||||
|
||||
Thread safety
|
||||
-------------
|
||||
|
||||
Jansson is thread safe and has no mutable global state. The only
|
||||
exceptions are the hash function seed and memory allocation functions,
|
||||
see below.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
If you want to make sure that two JSON value hierarchies do not
|
||||
contain shared values, use :func:`json_deep_copy()` to make copies.
|
||||
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
Locale
|
||||
------
|
||||
|
||||
Jansson works fine under any locale.
|
||||
|
||||
However, if the host program is multithreaded and uses ``setlocale()``
|
||||
to switch the locale in one thread while Jansson is currently encoding
|
||||
or decoding JSON data in another thread, the result may be wrong or
|
||||
the program may even crash.
|
||||
|
||||
Jansson uses locale specific functions for certain string conversions
|
||||
in the encoder and decoder, and then converts the locale specific
|
||||
values to/from the JSON representation. This fails if the locale
|
||||
changes between the string conversion and the locale-to-JSON
|
||||
conversion. This can only happen in multithreaded programs that use
|
||||
``setlocale()``, because ``setlocale()`` switches the locale for all
|
||||
running threads, not only the thread that calls ``setlocale()``.
|
||||
|
||||
If your program uses ``setlocale()`` as described above, consider
|
||||
using the thread-safe ``uselocale()`` instead.
|
||||
161
doc/tutorial.rst
161
doc/tutorial.rst
@@ -7,9 +7,8 @@ Tutorial
|
||||
.. highlight:: c
|
||||
|
||||
In this tutorial, we create a program that fetches the latest commits
|
||||
of a repository in GitHub_ over the web. One of the response formats
|
||||
supported by `GitHub API`_ is JSON, so the result can be parsed using
|
||||
Jansson.
|
||||
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
|
||||
parts of the program related to handling JSON data. For the best user
|
||||
@@ -31,42 +30,44 @@ name. Please note that the GitHub API is rate limited, so if you run
|
||||
the program too many times within a short period of time, the sever
|
||||
starts to respond with an error.
|
||||
|
||||
.. _GitHub: http://github.com/
|
||||
.. _GitHub API: http://develop.github.com/
|
||||
.. _GitHub: https://github.com/
|
||||
.. _GitHub API: http://developer.github.com/
|
||||
.. _libcurl: http://curl.haxx.se/
|
||||
|
||||
|
||||
.. _tutorial-github-commits-api:
|
||||
|
||||
The GitHub Commits API
|
||||
======================
|
||||
The GitHub Repo Commits API
|
||||
===========================
|
||||
|
||||
The `GitHub commits API`_ is used by sending HTTP requests to URLs
|
||||
starting with ``http://github.com/api/v2/json/commits/``. Our program
|
||||
only lists the latest commits, so the rest of the URL is
|
||||
``list/USER/REPOSITORY/BRANCH``, where ``USER``, ``REPOSITORY`` and
|
||||
``BRANCH`` are the GitHub user ID, the name of the repository, and the
|
||||
name of the branch whose commits are to be listed, respectively.
|
||||
The `GitHub Repo Commits API`_ is used by sending HTTP requests to
|
||||
URLs like ``https://api.github.com/repos/USER/REPOSITORY/commits``,
|
||||
where ``USER`` and ``REPOSITORY`` are the GitHub user ID and the name
|
||||
of the repository whose commits are to be listed, respectively.
|
||||
|
||||
GitHub responds with a JSON object of the following form:
|
||||
GitHub responds with a JSON array of the following form:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
"commits": [
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
[
|
||||
{
|
||||
"sha": "<the commit ID>",
|
||||
"commit": {
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
<more fields, not important to this tutorial...>
|
||||
},
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
<more fields...>
|
||||
},
|
||||
{
|
||||
"sha": "<the commit ID>",
|
||||
"commit": {
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
<more fields...>
|
||||
},
|
||||
<more commits...>
|
||||
]
|
||||
}
|
||||
<more fields...>
|
||||
},
|
||||
<more commits...>
|
||||
]
|
||||
|
||||
In our program, the HTTP request is sent using the following
|
||||
function::
|
||||
@@ -80,7 +81,7 @@ return value is *NULL*. For full details, refer to :download:`the code
|
||||
<github_commits.c>`, as the actual implementation is not important
|
||||
here.
|
||||
|
||||
.. _GitHub commits API: http://develop.github.com/p/commits.html
|
||||
.. _GitHub Repo Commits API: http://developer.github.com/v3/repos/commits/
|
||||
|
||||
.. _tutorial-the-program:
|
||||
|
||||
@@ -95,10 +96,10 @@ First the includes::
|
||||
Like all the programs using Jansson, we need to include
|
||||
:file:`jansson.h`.
|
||||
|
||||
The following definitions are used to build the GitHub commits API
|
||||
request URL::
|
||||
The following definitions are used to build the GitHub API request
|
||||
URL::
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
|
||||
#define URL_SIZE 256
|
||||
|
||||
The following function is used when formatting the result to find the
|
||||
@@ -118,20 +119,21 @@ first newline in the commit message::
|
||||
The main function follows. In the beginning, we first declare a bunch
|
||||
of variables and check the command line parameters::
|
||||
|
||||
size_t i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
size_t i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Then we build the request URL using the user and repository names
|
||||
given as command line parameters::
|
||||
@@ -171,47 +173,53 @@ Now we're ready to extract the data out of the decoded JSON response.
|
||||
The structure of the response JSON was explained in section
|
||||
:ref:`tutorial-github-commits-api`.
|
||||
|
||||
First, we'll extract the ``commits`` array from the JSON response::
|
||||
We check that the returned value really is an array::
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
if(!json_is_array(root))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
fprintf(stderr, "error: root is not an array\n");
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
This is the array that contains objects describing latest commits in
|
||||
the repository. We check that the returned value really is an array.
|
||||
If the key ``commits`` doesn't exist, :func:`json_object_get()`
|
||||
returns *NULL*, but :func:`json_is_array()` handles this case, too.
|
||||
|
||||
Then we proceed to loop over all the commits in the array::
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
for(i = 0; i < json_array_size(root); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
json_t *data, *sha, *commit, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
data = json_array_get(root, i);
|
||||
if(!json_is_object(data))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
The function :func:`json_array_size()` returns the size of a JSON
|
||||
array. First, we again declare some variables and then extract the
|
||||
i'th element of the ``commits`` array using :func:`json_array_get()`.
|
||||
i'th element of the ``root`` array using :func:`json_array_get()`.
|
||||
We also check that the resulting value is a JSON object.
|
||||
|
||||
Next we'll extract the commit ID and commit message, and check that
|
||||
they both are JSON strings::
|
||||
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::
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
sha = json_object_get(data, "sha");
|
||||
if(!json_is_string(sha))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1);
|
||||
json_decref(root);
|
||||
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);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -219,6 +227,7 @@ they both are JSON strings::
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
json_decref(root);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
@@ -250,25 +259,27 @@ The program's ready, let's test it and view the latest commits in
|
||||
Jansson's repository::
|
||||
|
||||
$ ./github_commits akheron jansson
|
||||
86dc1d62 Fix indentation
|
||||
b67e130f json_dumpf: Document the output shortage on error
|
||||
4cd77771 Enhance handling of circular references
|
||||
79009e62 json_dumps: Close the strbuffer if dumping fails
|
||||
76999799 doc: Fix a small typo in apiref
|
||||
22af193a doc/Makefile.am: Remove *.pyc in clean
|
||||
951d091f Make integer, real and string mutable
|
||||
185e107d Don't use non-portable asprintf()
|
||||
ca7703fb Merge branch '1.0'
|
||||
12cd4e8c jansson 1.0.4
|
||||
<etc...>
|
||||
1581f26a Merge branch '2.3'
|
||||
aabfd493 load: Change buffer_pos to be a size_t
|
||||
bd72efbd load: Avoid unexpected behaviour in macro expansion
|
||||
e8fd3e30 Document and tweak json_load_callback()
|
||||
873eddaf Merge pull request #60 from rogerz/contrib
|
||||
bd2c0c73 Ignore the binary test_load_callback
|
||||
17a51a4b Merge branch '2.3'
|
||||
09c39adc Add json_load_callback to the list of exported symbols
|
||||
cbb80baf Merge pull request #57 from rogerz/contrib
|
||||
040bd7b0 Add json_load_callback()
|
||||
2637faa4 Make test stripping locale independent
|
||||
<...>
|
||||
|
||||
|
||||
Conclusion
|
||||
==========
|
||||
|
||||
In this tutorial, we implemented a program that fetches the latest
|
||||
commits of a GitHub repository using the GitHub commits API. Jansson
|
||||
was used to decode the JSON response and to extract the commit data.
|
||||
commits of a GitHub repository using the GitHub Repo Commits API.
|
||||
Jansson was used to decode the JSON response and to extract the commit
|
||||
data.
|
||||
|
||||
This tutorial only covered a small part of Jansson. For example, we
|
||||
did not create or manipulate JSON values at all. Proceed to
|
||||
|
||||
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,3 +1,5 @@
|
||||
EXTRA_DIST = jansson.def
|
||||
|
||||
include_HEADERS = jansson.h jansson_config.h
|
||||
|
||||
lib_LTLIBRARIES = libjansson.la
|
||||
@@ -6,20 +8,19 @@ 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 \
|
||||
strbuffer.h \
|
||||
strconv.c \
|
||||
utf.c \
|
||||
utf.h \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = \
|
||||
-no-undefined \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 4:1:0
|
||||
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
|
||||
endif
|
||||
-version-info 10:0:6
|
||||
|
||||
119
src/dump.c
119
src/dump.c
@@ -1,17 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
@@ -19,21 +22,17 @@
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
typedef int (*dump_func)(const char *buffer, int size, void *data);
|
||||
|
||||
struct string
|
||||
{
|
||||
char *buffer;
|
||||
int length;
|
||||
int size;
|
||||
struct object_key {
|
||||
size_t serial;
|
||||
const char *key;
|
||||
};
|
||||
|
||||
static int dump_to_strbuffer(const char *buffer, int size, void *data)
|
||||
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
|
||||
}
|
||||
|
||||
static int dump_to_file(const char *buffer, int size, void *data)
|
||||
static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
FILE *dest = (FILE *)data;
|
||||
if(fwrite(buffer, size, 1, dest) != 1)
|
||||
@@ -42,9 +41,9 @@ static int dump_to_file(const char *buffer, int size, void *data)
|
||||
}
|
||||
|
||||
/* 32 spaces (the maximum indentation size) */
|
||||
static char whitespace[] = " ";
|
||||
static const char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
|
||||
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
{
|
||||
@@ -66,7 +65,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
|
||||
{
|
||||
const char *pos, *end;
|
||||
int32_t codepoint;
|
||||
@@ -91,8 +90,12 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
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;
|
||||
@@ -106,7 +109,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
if(end == pos)
|
||||
break;
|
||||
|
||||
/* handle \, ", and control codes */
|
||||
/* handle \, /, ", and control codes */
|
||||
length = 2;
|
||||
switch(codepoint)
|
||||
{
|
||||
@@ -117,6 +120,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
case '\n': text = "\\n"; break;
|
||||
case '\r': text = "\\r"; break;
|
||||
case '\t': text = "\\t"; break;
|
||||
case '/': text = "\\/"; break;
|
||||
default:
|
||||
{
|
||||
/* codepoint is in BMP */
|
||||
@@ -155,20 +159,23 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
|
||||
static int object_key_compare_keys(const void *key1, const void *key2)
|
||||
{
|
||||
return strcmp((*(const object_key_t **)key1)->key,
|
||||
(*(const object_key_t **)key2)->key);
|
||||
return strcmp(((const struct object_key *)key1)->key,
|
||||
((const struct object_key *)key2)->key);
|
||||
}
|
||||
|
||||
static int object_key_compare_serials(const void *key1, const void *key2)
|
||||
{
|
||||
return (*(const object_key_t **)key1)->serial -
|
||||
(*(const object_key_t **)key2)->serial;
|
||||
size_t a = ((const struct object_key *)key1)->serial;
|
||||
size_t b = ((const struct object_key *)key2)->serial;
|
||||
|
||||
return a < b ? -1 : a == b ? 0 : 1;
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
dump_func dump, void *data)
|
||||
json_dump_callback_t dump, void *data)
|
||||
{
|
||||
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
|
||||
if(!json)
|
||||
return -1;
|
||||
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_NULL:
|
||||
@@ -188,7 +195,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,31 +205,17 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
{
|
||||
char buffer[MAX_REAL_STR_LENGTH];
|
||||
int size;
|
||||
double value = json_real_value(json);
|
||||
|
||||
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
|
||||
json_real_value(json));
|
||||
if(size >= MAX_REAL_STR_LENGTH)
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
|
||||
if(size < 0)
|
||||
return -1;
|
||||
|
||||
/* Make sure there's a dot or 'e' in the output. Otherwise
|
||||
a real is converted to an integer when decoding */
|
||||
if(strchr(buffer, '.') == NULL &&
|
||||
strchr(buffer, 'e') == NULL)
|
||||
{
|
||||
if(size + 2 >= MAX_REAL_STR_LENGTH) {
|
||||
/* No space to append ".0" */
|
||||
return -1;
|
||||
}
|
||||
buffer[size] = '.';
|
||||
buffer[size + 1] = '0';
|
||||
size += 2;
|
||||
}
|
||||
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), ascii, dump, data);
|
||||
return dump_string(json_string_value(json), dump, data, flags);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
@@ -308,19 +301,20 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
|
||||
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
|
||||
{
|
||||
const object_key_t **keys;
|
||||
struct object_key *keys;
|
||||
size_t size, i;
|
||||
int (*cmp_func)(const void *, const void *);
|
||||
|
||||
size = json_object_size(json);
|
||||
keys = jsonp_malloc(size * sizeof(object_key_t *));
|
||||
keys = jsonp_malloc(size * sizeof(struct object_key));
|
||||
if(!keys)
|
||||
goto object_error;
|
||||
|
||||
i = 0;
|
||||
while(iter)
|
||||
{
|
||||
keys[i] = jsonp_object_iter_fullkey(iter);
|
||||
keys[i].serial = hashtable_iter_serial(iter);
|
||||
keys[i].key = json_object_iter_key(iter);
|
||||
iter = json_object_iter_next((json_t *)json, iter);
|
||||
i++;
|
||||
}
|
||||
@@ -331,18 +325,18 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
else
|
||||
cmp_func = object_key_compare_serials;
|
||||
|
||||
qsort(keys, size, sizeof(object_key_t *), cmp_func);
|
||||
qsort(keys, size, sizeof(struct object_key), cmp_func);
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = keys[i]->key;
|
||||
key = keys[i].key;
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, ascii, dump, data);
|
||||
dump_string(key, dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
@@ -379,7 +373,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), ascii, dump, data);
|
||||
dump_string(json_object_iter_key(iter), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
@@ -415,35 +409,26 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *json_dumps(const json_t *json, size_t flags)
|
||||
{
|
||||
strbuffer_t strbuff;
|
||||
char *result;
|
||||
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return NULL;
|
||||
|
||||
if(strbuffer_init(&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
|
||||
result = NULL;
|
||||
else
|
||||
result = jsonp_strdup(strbuffer_value(&strbuff));
|
||||
|
||||
result = jsonp_strdup(strbuffer_value(&strbuff));
|
||||
strbuffer_close(&strbuff);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
{
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
|
||||
return do_dump(json, flags, 0, dump_to_file, (void *)output);
|
||||
return json_dump_callback(json, dump_to_file, (void *)output, flags);
|
||||
}
|
||||
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
@@ -459,3 +444,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
fclose(output);
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
||||
{
|
||||
if(!(flags & JSON_ENCODE_ANY)) {
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return do_dump(json, flags, 0, callback, data);
|
||||
}
|
||||
|
||||
@@ -59,4 +59,5 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
error->position = position;
|
||||
|
||||
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
||||
}
|
||||
|
||||
149
src/hashtable.c
149
src/hashtable.c
@@ -1,11 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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 <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"
|
||||
@@ -14,7 +24,13 @@ 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)
|
||||
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
|
||||
|
||||
static JSON_INLINE void list_init(list_t *list)
|
||||
{
|
||||
@@ -56,22 +72,8 @@ 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 const size_t num_primes = sizeof(primes) / sizeof(size_t);
|
||||
|
||||
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 void *key, size_t hash)
|
||||
const char *key, size_t hash)
|
||||
{
|
||||
list_t *list;
|
||||
pair_t *pair;
|
||||
@@ -83,7 +85,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
while(1)
|
||||
{
|
||||
pair = list_to_pair(list);
|
||||
if(pair->hash == hash && hashtable->cmp_keys(pair->key, key))
|
||||
if(pair->hash == hash && strcmp(pair->key, key) == 0)
|
||||
return pair;
|
||||
|
||||
if(list == bucket->last)
|
||||
@@ -97,13 +99,13 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
|
||||
/* returns 0 on success, -1 if key was not found */
|
||||
static int hashtable_do_del(hashtable_t *hashtable,
|
||||
const void *key, size_t hash)
|
||||
const char *key, size_t hash)
|
||||
{
|
||||
pair_t *pair;
|
||||
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);
|
||||
@@ -120,11 +122,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
bucket->last = pair->list.prev;
|
||||
|
||||
list_remove(&pair->list);
|
||||
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
json_decref(pair->value);
|
||||
|
||||
jsonp_free(pair);
|
||||
hashtable->size--;
|
||||
@@ -141,10 +139,7 @@ static void hashtable_do_clear(hashtable_t *hashtable)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
json_decref(pair->value);
|
||||
jsonp_free(pair);
|
||||
}
|
||||
}
|
||||
@@ -157,14 +152,14 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
|
||||
jsonp_free(hashtable->buckets);
|
||||
|
||||
hashtable->num_buckets++;
|
||||
new_size = num_buckets(hashtable);
|
||||
hashtable->order++;
|
||||
new_size = hashsize(hashtable->order);
|
||||
|
||||
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
@@ -184,48 +179,19 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
}
|
||||
|
||||
|
||||
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
free_fn free_key, free_fn free_value)
|
||||
{
|
||||
hashtable_t *hashtable = jsonp_malloc(sizeof(hashtable_t));
|
||||
if(!hashtable)
|
||||
return NULL;
|
||||
|
||||
if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
|
||||
{
|
||||
jsonp_free(hashtable);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hashtable;
|
||||
}
|
||||
|
||||
void hashtable_destroy(hashtable_t *hashtable)
|
||||
{
|
||||
hashtable_close(hashtable);
|
||||
jsonp_free(hashtable);
|
||||
}
|
||||
|
||||
int hashtable_init(hashtable_t *hashtable,
|
||||
key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
free_fn free_key, free_fn free_value)
|
||||
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 = 3;
|
||||
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
list_init(&hashtable->list);
|
||||
|
||||
hashtable->hash_key = hash_key;
|
||||
hashtable->cmp_keys = cmp_keys;
|
||||
hashtable->free_key = free_key;
|
||||
hashtable->free_value = free_value;
|
||||
|
||||
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;
|
||||
@@ -240,39 +206,42 @@ void hashtable_close(hashtable_t *hashtable)
|
||||
jsonp_free(hashtable->buckets);
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
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 = hashtable->hash_key(key);
|
||||
index = hash % num_buckets(hashtable);
|
||||
hash = hash_str(key);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
|
||||
if(pair)
|
||||
{
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
json_decref(pair->value);
|
||||
pair->value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
pair = jsonp_malloc(sizeof(pair_t));
|
||||
/* 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);
|
||||
if(!pair)
|
||||
return -1;
|
||||
|
||||
pair->key = key;
|
||||
pair->value = value;
|
||||
pair->hash = hash;
|
||||
pair->serial = serial;
|
||||
strcpy(pair->key, key);
|
||||
pair->value = value;
|
||||
list_init(&pair->list);
|
||||
|
||||
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||
@@ -282,14 +251,14 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *hashtable_get(hashtable_t *hashtable, const void *key)
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key)
|
||||
{
|
||||
pair_t *pair;
|
||||
size_t hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
@@ -298,9 +267,9 @@ void *hashtable_get(hashtable_t *hashtable, const void *key)
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
int hashtable_del(hashtable_t *hashtable, const void *key)
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key)
|
||||
{
|
||||
size_t hash = hashtable->hash_key(key);
|
||||
size_t hash = hash_str(key);
|
||||
return hashtable_do_del(hashtable, key, hash);
|
||||
}
|
||||
|
||||
@@ -310,7 +279,7 @@ 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;
|
||||
@@ -325,14 +294,14 @@ void *hashtable_iter(hashtable_t *hashtable)
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
{
|
||||
pair_t *pair;
|
||||
size_t hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
@@ -355,18 +324,22 @@ void *hashtable_iter_key(void *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);
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
|
||||
void hashtable_iter_set(void *iter, json_t *value)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
|
||||
json_decref(pair->value);
|
||||
pair->value = value;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,20 +8,20 @@
|
||||
#ifndef HASHTABLE_H
|
||||
#define HASHTABLE_H
|
||||
|
||||
typedef size_t (*key_hash_fn)(const void *key);
|
||||
typedef int (*key_cmp_fn)(const void *key1, const void *key2);
|
||||
typedef void (*free_fn)(void *key);
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
};
|
||||
|
||||
/* "pair" may be a bit confusing a name, but think of it as a
|
||||
key-value pair. In this case, it just encodes some extra data,
|
||||
too */
|
||||
struct hashtable_pair {
|
||||
void *key;
|
||||
void *value;
|
||||
size_t hash;
|
||||
struct hashtable_list list;
|
||||
json_t *value;
|
||||
size_t serial;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
struct hashtable_bucket {
|
||||
@@ -32,58 +32,26 @@ 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;
|
||||
|
||||
key_hash_fn hash_key;
|
||||
key_cmp_fn cmp_keys; /* returns non-zero for equal keys */
|
||||
free_fn free_key;
|
||||
free_fn free_value;
|
||||
} hashtable_t;
|
||||
|
||||
/**
|
||||
* hashtable_create - Create a hashtable object
|
||||
*
|
||||
* @hash_key: The key hashing function
|
||||
* @cmp_keys: The key compare function. Returns non-zero for equal and
|
||||
* zero for unequal unequal keys
|
||||
* @free_key: If non-NULL, called for a key that is no longer referenced.
|
||||
* @free_value: If non-NULL, called for a value that is no longer referenced.
|
||||
*
|
||||
* Returns a new hashtable object that should be freed with
|
||||
* hashtable_destroy when it's no longer used, or NULL on failure (out
|
||||
* of memory).
|
||||
*/
|
||||
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
free_fn free_key, free_fn free_value);
|
||||
|
||||
/**
|
||||
* hashtable_destroy - Destroy a hashtable object
|
||||
*
|
||||
* @hashtable: The hashtable
|
||||
*
|
||||
* Destroys a hashtable created with hashtable_create().
|
||||
*/
|
||||
void hashtable_destroy(hashtable_t *hashtable);
|
||||
#define hashtable_key_to_iter(key_) \
|
||||
(&(container_of(key_, struct hashtable_pair, key)->list))
|
||||
|
||||
|
||||
/**
|
||||
* hashtable_init - Initialize a hashtable object
|
||||
*
|
||||
* @hashtable: The (statically allocated) hashtable object
|
||||
* @hash_key: The key hashing function
|
||||
* @cmp_keys: The key compare function. Returns non-zero for equal and
|
||||
* zero for unequal unequal keys
|
||||
* @free_key: If non-NULL, called for a key that is no longer referenced.
|
||||
* @free_value: If non-NULL, called for a value that is no longer referenced.
|
||||
*
|
||||
* Initializes a statically allocated hashtable object. The object
|
||||
* should be cleared with hashtable_close when it's no longer used.
|
||||
*
|
||||
* Returns 0 on success, -1 on error (out of memory).
|
||||
*/
|
||||
int hashtable_init(hashtable_t *hashtable,
|
||||
key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
free_fn free_key, free_fn free_value);
|
||||
int hashtable_init(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_close - Release all resources used by a hashtable object
|
||||
@@ -99,20 +67,19 @@ void hashtable_close(hashtable_t *hashtable);
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
* @serial: For addition order of keys
|
||||
* @value: The value
|
||||
*
|
||||
* If a value with the given key already exists, its value is replaced
|
||||
* with the new value.
|
||||
*
|
||||
* Key and value are "stealed" in the sense that hashtable frees them
|
||||
* automatically when they are no longer used. The freeing is
|
||||
* accomplished by calling free_key and free_value functions that were
|
||||
* supplied to hashtable_new. In case one or both of the free
|
||||
* functions is NULL, the corresponding item is not "stealed".
|
||||
* with the new value. Value is "stealed" in the sense that hashtable
|
||||
* doesn't increment its refcount but decreases the refcount when the
|
||||
* value is no longer needed.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure (out of memory).
|
||||
*/
|
||||
int hashtable_set(hashtable_t *hashtable, void *key, void *value);
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value);
|
||||
|
||||
/**
|
||||
* hashtable_get - Get a value associated with a key
|
||||
@@ -122,7 +89,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value);
|
||||
*
|
||||
* Returns value if it is found, or NULL otherwise.
|
||||
*/
|
||||
void *hashtable_get(hashtable_t *hashtable, const void *key);
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key);
|
||||
|
||||
/**
|
||||
* hashtable_del - Remove a value from the hashtable
|
||||
@@ -132,7 +99,7 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
|
||||
*
|
||||
* Returns 0 on success, or -1 if the key was not found.
|
||||
*/
|
||||
int hashtable_del(hashtable_t *hashtable, const void *key);
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key);
|
||||
|
||||
/**
|
||||
* hashtable_clear - Clear hashtable
|
||||
@@ -169,7 +136,7 @@ void *hashtable_iter(hashtable_t *hashtable);
|
||||
* Like hashtable_iter() but returns an iterator pointing to a
|
||||
* specific key.
|
||||
*/
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
|
||||
|
||||
/**
|
||||
* hashtable_iter_next - Advance an iterator
|
||||
@@ -189,6 +156,13 @@ 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
|
||||
*
|
||||
@@ -202,6 +176,6 @@ void *hashtable_iter_value(void *iter);
|
||||
* @iter: The iterator
|
||||
* @value: The value to set
|
||||
*/
|
||||
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
|
||||
void hashtable_iter_set(void *iter, json_t *value);
|
||||
|
||||
#endif
|
||||
|
||||
278
src/hashtable_seed.c
Normal file
278
src/hashtable_seed.c
Normal file
@@ -0,0 +1,278 @@
|
||||
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
|
||||
the hash function.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <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 _getpid() */
|
||||
#include <process.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 <windows.h>
|
||||
#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("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 = CryptGenRandom(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)_getpid();
|
||||
#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 (!done && seed_from_urandom(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
if (!done && 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
|
||||
64
src/jansson.def
Normal file
64
src/jansson.def
Normal file
@@ -0,0 +1,64 @@
|
||||
EXPORTS
|
||||
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_object_seed
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
json_dump_callback
|
||||
json_loads
|
||||
json_loadb
|
||||
json_loadf
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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.
|
||||
@@ -21,11 +21,11 @@ extern "C" {
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 0
|
||||
#define JANSSON_MICRO_VERSION 1
|
||||
#define JANSSON_MINOR_VERSION 6
|
||||
#define JANSSON_MICRO_VERSION 0
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.0.1"
|
||||
#define JANSSON_VERSION "2.6"
|
||||
|
||||
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
|
||||
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
|
||||
@@ -47,18 +47,24 @@ typedef enum {
|
||||
JSON_NULL
|
||||
} json_type;
|
||||
|
||||
typedef struct {
|
||||
typedef struct json_t {
|
||||
json_type type;
|
||||
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"
|
||||
#else
|
||||
#define JSON_INTEGER_FORMAT "lld"
|
||||
#endif
|
||||
typedef long long json_int_t;
|
||||
#else
|
||||
#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)
|
||||
@@ -82,6 +88,7 @@ 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);
|
||||
|
||||
static JSON_INLINE
|
||||
@@ -119,6 +126,7 @@ typedef struct {
|
||||
|
||||
/* 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);
|
||||
@@ -126,13 +134,26 @@ int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
|
||||
int json_object_del(json_t *object, const char *key);
|
||||
int json_object_clear(json_t *object);
|
||||
int json_object_update(json_t *object, json_t *other);
|
||||
int json_object_update_existing(json_t *object, json_t *other);
|
||||
int json_object_update_missing(json_t *object, json_t *other);
|
||||
void *json_object_iter(json_t *object);
|
||||
void *json_object_iter_at(json_t *object, const char *key);
|
||||
void *json_object_key_to_iter(const char *key);
|
||||
void *json_object_iter_next(json_t *object, void *iter);
|
||||
const char *json_object_iter_key(void *iter);
|
||||
json_t *json_object_iter_value(void *iter);
|
||||
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
|
||||
#define json_object_foreach(object, key, value) \
|
||||
for(key = json_object_iter_key(json_object_iter(object)); \
|
||||
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
|
||||
key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
|
||||
|
||||
#define json_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)
|
||||
{
|
||||
@@ -161,9 +182,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
|
||||
@@ -173,9 +194,9 @@ 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);
|
||||
@@ -211,25 +232,41 @@ int json_equal(json_t *value1, 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);
|
||||
|
||||
|
||||
/* loading, printing */
|
||||
/* decoding */
|
||||
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
#define JSON_DECODE_ANY 0x4
|
||||
#define JSON_DECODE_INT_AS_REAL 0x8
|
||||
|
||||
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
|
||||
|
||||
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
|
||||
json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
#define JSON_ESCAPE_SLASH 0x400
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
char *json_dumps(const json_t *json, size_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags);
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags);
|
||||
|
||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
|
||||
|
||||
/* custom memory allocation */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2013 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.
|
||||
@@ -27,8 +27,13 @@
|
||||
#define JSON_INLINE @json_inline@
|
||||
#endif
|
||||
|
||||
/* If your compiler supports the `long long` type,
|
||||
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
|
||||
/* If your compiler supports the `long long` type and the strtoll()
|
||||
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
|
||||
|
||||
/* If locale.h and localeconv() are available, define to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,6 +11,7 @@
|
||||
#include <stddef.h>
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
#include "strbuffer.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
|
||||
@@ -66,16 +67,6 @@ typedef struct {
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
size_t jsonp_hash_str(const void *ptr);
|
||||
int jsonp_str_equal(const void *ptr1, const void *ptr2);
|
||||
|
||||
typedef struct {
|
||||
size_t serial;
|
||||
char key[1];
|
||||
} object_key_t;
|
||||
|
||||
const object_key_t *jsonp_object_iter_fullkey(void *iter);
|
||||
|
||||
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,
|
||||
@@ -83,9 +74,20 @@ void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
size_t position, 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Windows compatibility */
|
||||
#ifdef _WIN32
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
285
src/load.c
285
src/load.c
@@ -1,12 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
@@ -14,7 +16,7 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
@@ -32,6 +34,14 @@
|
||||
#define TOKEN_FALSE 260
|
||||
#define TOKEN_NULL 261
|
||||
|
||||
/* Locale independent versions of isxxx() functions */
|
||||
#define l_isupper(c) ('A' <= (c) && (c) <= 'Z')
|
||||
#define l_islower(c) ('a' <= (c) && (c) <= 'z')
|
||||
#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'))
|
||||
|
||||
/* Read one byte from stream, convert to unsigned char, then int, and
|
||||
return. return EOF on end of file. This corresponds to the
|
||||
behaviour of fgetc(). */
|
||||
@@ -41,7 +51,7 @@ typedef struct {
|
||||
get_func get;
|
||||
void *data;
|
||||
char buffer[5];
|
||||
int buffer_pos;
|
||||
size_t buffer_pos;
|
||||
int state;
|
||||
int line;
|
||||
int column, last_column;
|
||||
@@ -69,6 +79,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
||||
{
|
||||
va_list ap;
|
||||
char msg_text[JSON_ERROR_TEXT_LENGTH];
|
||||
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
|
||||
|
||||
int line = -1, col = -1;
|
||||
size_t pos = 0;
|
||||
@@ -79,12 +90,12 @@ 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)
|
||||
{
|
||||
const char *saved_text = strbuffer_value(&lex->saved_text);
|
||||
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
|
||||
|
||||
line = lex->stream.line;
|
||||
col = lex->stream.column;
|
||||
@@ -95,6 +106,7 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -107,6 +119,7 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -240,9 +253,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);
|
||||
}
|
||||
}
|
||||
@@ -268,11 +290,11 @@ static int32_t decode_unicode_escape(const char *str)
|
||||
for(i = 1; i <= 4; i++) {
|
||||
char c = str[i];
|
||||
value <<= 4;
|
||||
if(isdigit(c))
|
||||
if(l_isdigit(c))
|
||||
value += c - '0';
|
||||
else if(islower(c))
|
||||
else if(l_islower(c))
|
||||
value += c - 'a' + 10;
|
||||
else if(isupper(c))
|
||||
else if(l_isupper(c))
|
||||
value += c - 'A' + 10;
|
||||
else
|
||||
assert(0);
|
||||
@@ -317,7 +339,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(c == 'u') {
|
||||
c = lex_get_save(lex, error);
|
||||
for(i = 0; i < 4; i++) {
|
||||
if(!isxdigit(c)) {
|
||||
if(!l_isxdigit(c)) {
|
||||
error_set(error, lex, "invalid escape");
|
||||
goto out;
|
||||
}
|
||||
@@ -436,11 +458,17 @@ out:
|
||||
jsonp_free(lex->value.string);
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -455,14 +483,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
|
||||
if(c == '0') {
|
||||
c = lex_get_save(lex, error);
|
||||
if(isdigit(c)) {
|
||||
if(l_isdigit(c)) {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if(isdigit(c)) {
|
||||
else if(l_isdigit(c)) {
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
while(l_isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
else {
|
||||
@@ -496,14 +524,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
|
||||
if(c == '.') {
|
||||
c = lex_get(lex, error);
|
||||
if(!isdigit(c)) {
|
||||
if(!l_isdigit(c)) {
|
||||
lex_unget(lex, c);
|
||||
goto out;
|
||||
}
|
||||
lex_save(lex, c);
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
while(l_isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
|
||||
@@ -512,23 +540,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
if(c == '+' || c == '-')
|
||||
c = lex_get_save(lex, error);
|
||||
|
||||
if(!isdigit(c)) {
|
||||
if(!l_isdigit(c)) {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
while(l_isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
value = strtod(saved_text, &end);
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
if(jsonp_strtod(&lex->saved_text, &value)) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
@@ -574,17 +598,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
else if(c == '"')
|
||||
lex_scan_string(lex, error);
|
||||
|
||||
else if(isdigit(c) || c == '-') {
|
||||
else if(l_isdigit(c) || c == '-') {
|
||||
if(lex_scan_number(lex, c, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
else if(isupper(c) || islower(c)) {
|
||||
else if(l_isalpha(c)) {
|
||||
/* eat up the whole identifier for clearer error messages */
|
||||
const char *saved_text;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(isupper(c) || islower(c))
|
||||
while(l_isalpha(c))
|
||||
c = lex_get_save(lex, error);
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
@@ -642,9 +666,9 @@ static void lex_close(lex_t *lex)
|
||||
|
||||
/*** parser ***/
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error);
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
|
||||
|
||||
static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *object = json_object();
|
||||
if(!object)
|
||||
@@ -667,6 +691,14 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "duplicate object key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ':') {
|
||||
jsonp_free(key);
|
||||
@@ -675,7 +707,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
value = parse_value(lex, error);
|
||||
value = parse_value(lex, flags, error);
|
||||
if(!value) {
|
||||
jsonp_free(key);
|
||||
goto error;
|
||||
@@ -709,7 +741,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *array = json_array();
|
||||
if(!array)
|
||||
@@ -720,7 +752,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
return array;
|
||||
|
||||
while(lex->token) {
|
||||
json_t *elem = parse_value(lex, error);
|
||||
json_t *elem = parse_value(lex, flags, error);
|
||||
if(!elem)
|
||||
goto error;
|
||||
|
||||
@@ -749,9 +781,10 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
double value;
|
||||
|
||||
switch(lex->token) {
|
||||
case TOKEN_STRING: {
|
||||
@@ -760,7 +793,15 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
}
|
||||
|
||||
case TOKEN_INTEGER: {
|
||||
json = json_integer(lex->value.integer);
|
||||
if (flags & JSON_DECODE_INT_AS_REAL) {
|
||||
if(jsonp_strtod(&lex->saved_text, &value)) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
return NULL;
|
||||
}
|
||||
json = json_real(value);
|
||||
} else {
|
||||
json = json_integer(lex->value.integer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -782,11 +823,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, flags, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
json = parse_array(lex, error);
|
||||
json = parse_array(lex, flags, error);
|
||||
break;
|
||||
|
||||
case TOKEN_INVALID:
|
||||
@@ -804,15 +845,37 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
return json;
|
||||
}
|
||||
|
||||
static json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
error_set(error, lex, "'[' or '{' expected");
|
||||
return NULL;
|
||||
if(!(flags & JSON_DECODE_ANY)) {
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
error_set(error, lex, "'[' or '{' expected");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return parse_value(lex, error);
|
||||
result = parse_value(lex, flags, error);
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != TOKEN_EOF) {
|
||||
error_set(error, lex, "end of file expected");
|
||||
json_decref(result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(error) {
|
||||
/* Save the position even though there was no error */
|
||||
error->position = lex->stream.position;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@@ -841,7 +904,12 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
json_t *result;
|
||||
string_data_t stream_data;
|
||||
|
||||
(void)flags; /* unused */
|
||||
jsonp_error_init(error, "<string>");
|
||||
|
||||
if (string == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
@@ -849,20 +917,53 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
if(lex_init(&lex, string_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
jsonp_error_init(error, "<string>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
typedef struct
|
||||
{
|
||||
const char *data;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
} buffer_data_t;
|
||||
|
||||
static int buffer_get(void *data)
|
||||
{
|
||||
char c;
|
||||
buffer_data_t *stream = data;
|
||||
if(stream->pos >= stream->len)
|
||||
return EOF;
|
||||
|
||||
c = stream->data[stream->pos];
|
||||
stream->pos++;
|
||||
return (unsigned char)c;
|
||||
}
|
||||
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
buffer_data_t stream_data;
|
||||
|
||||
jsonp_error_init(error, "<buffer>");
|
||||
|
||||
if (buffer == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
stream_data.data = buffer;
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
||||
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
@@ -872,10 +973,6 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
(void)flags; /* unused */
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
return NULL;
|
||||
|
||||
if(input == stdin)
|
||||
source = "<stdin>";
|
||||
@@ -884,18 +981,16 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
if (input == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
@@ -907,7 +1002,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
|
||||
jsonp_error_init(error, path);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (path == NULL) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if(!fp)
|
||||
{
|
||||
error_set(error, NULL, "unable to open %s: %s",
|
||||
@@ -920,3 +1020,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, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)callback_get, &stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
366
src/lookup3.h
Normal file
366
src/lookup3.h
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
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 <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 */
|
||||
|
||||
#ifdef VALGRIND
|
||||
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 VALGRIND
|
||||
|
||||
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;
|
||||
case 11: c+=((uint32_t)k[10])<<16;
|
||||
case 10: c+=((uint32_t)k[9])<<8;
|
||||
case 9 : c+=k[8];
|
||||
case 8 : b+=((uint32_t)k[7])<<24;
|
||||
case 7 : b+=((uint32_t)k[6])<<16;
|
||||
case 6 : b+=((uint32_t)k[5])<<8;
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((uint32_t)k[3])<<24;
|
||||
case 3 : a+=((uint32_t)k[2])<<16;
|
||||
case 2 : a+=((uint32_t)k[1])<<8;
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return c;
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return c;
|
||||
}
|
||||
15
src/memory.c
15
src/memory.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011 Basile Starynkevitch <basile@starynkevitch.net>
|
||||
* Copyright (c) 2009-2013 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
|
||||
* under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
|
||||
/* memory function pointers */
|
||||
@@ -35,12 +35,17 @@ void jsonp_free(void *ptr)
|
||||
char *jsonp_strdup(const char *str)
|
||||
{
|
||||
char *new_str;
|
||||
size_t len;
|
||||
|
||||
new_str = jsonp_malloc(strlen(str) + 1);
|
||||
len = strlen(str);
|
||||
if(len == (size_t)-1)
|
||||
return NULL;
|
||||
|
||||
new_str = jsonp_malloc(len + 1);
|
||||
if(!new_str)
|
||||
return NULL;
|
||||
|
||||
strcpy(new_str, str);
|
||||
memcpy(new_str, str, len + 1);
|
||||
return new_str;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
* Copyright (c) 2009-2013 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
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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;
|
||||
} scanner_t;
|
||||
|
||||
static const char *type_names[] = {
|
||||
#define token(scanner) ((scanner)->token.token)
|
||||
|
||||
static const char * const type_names[] = {
|
||||
"object",
|
||||
"array",
|
||||
"string",
|
||||
@@ -34,7 +46,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,
|
||||
@@ -43,14 +55,28 @@ 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;
|
||||
}
|
||||
|
||||
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 +87,32 @@ 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 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, 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,
|
||||
fmt, ap);
|
||||
|
||||
jsonp_error_set_source(s->error, source);
|
||||
|
||||
@@ -86,35 +121,107 @@ 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, int *ours)
|
||||
{
|
||||
char t;
|
||||
strbuffer_t strbuff;
|
||||
const char *str;
|
||||
size_t length;
|
||||
char *result;
|
||||
|
||||
next_token(s);
|
||||
t = token(s);
|
||||
prev_token(s);
|
||||
|
||||
if(t != '#' && t != '+') {
|
||||
/* Optimize the simple case */
|
||||
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 %s", purpose);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ours = 0;
|
||||
return (char *)str;
|
||||
}
|
||||
|
||||
strbuffer_init(&strbuff);
|
||||
|
||||
while(1) {
|
||||
str = va_arg(*ap, const char *);
|
||||
if(!str) {
|
||||
set_error(s, "<args>", "NULL string argument");
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '#') {
|
||||
length = va_arg(*ap, int);
|
||||
}
|
||||
else {
|
||||
prev_token(s);
|
||||
length = strlen(str);
|
||||
}
|
||||
|
||||
if(strbuffer_append_bytes(&strbuff, str, length) == -1) {
|
||||
set_error(s, "<internal>", "Out of memory");
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
if(token(s) != '+') {
|
||||
prev_token(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = strbuffer_steal_value(&strbuff);
|
||||
|
||||
if(!utf8_check_string(result, -1)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ours = 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
int ours;
|
||||
json_t *value;
|
||||
|
||||
if(!s->token) {
|
||||
if(!token(s)) {
|
||||
set_error(s, "<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>", "Expected format 's', got '%c'", token(s));
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = va_arg(*ap, const char *);
|
||||
if(!key) {
|
||||
set_error(s, "<args>", "NULL object key");
|
||||
key = read_string(s, ap, "object key", &ours);
|
||||
if(!key)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(!utf8_check_string(key, -1)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 in object key");
|
||||
goto error;
|
||||
}
|
||||
|
||||
next_token(s);
|
||||
|
||||
@@ -123,10 +230,16 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
goto error;
|
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
@@ -142,10 +255,10 @@ 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) {
|
||||
if(!token(s)) {
|
||||
set_error(s, "<format>", "Unexpected end of format string");
|
||||
goto error;
|
||||
}
|
||||
@@ -170,25 +283,27 @@ error:
|
||||
|
||||
static json_t *pack(scanner_t *s, va_list *ap)
|
||||
{
|
||||
switch(s->token) {
|
||||
switch(token(s)) {
|
||||
case '{':
|
||||
return pack_object(s, ap);
|
||||
|
||||
case '[':
|
||||
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");
|
||||
case 's': { /* string */
|
||||
char *str;
|
||||
int ours;
|
||||
json_t *result;
|
||||
|
||||
str = read_string(s, ap, "string", &ours);
|
||||
if(!str)
|
||||
return NULL;
|
||||
}
|
||||
if(!utf8_check_string(str, -1)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 string");
|
||||
return NULL;
|
||||
}
|
||||
return json_string_nocheck(str);
|
||||
|
||||
result = json_string_nocheck(str);
|
||||
if(ours)
|
||||
jsonp_free(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case 'n': /* null */
|
||||
@@ -214,7 +329,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
|
||||
default:
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
s->token);
|
||||
token(s));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -233,41 +348,42 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
*/
|
||||
hashtable_t key_set;
|
||||
|
||||
if(hashtable_init(&key_set, jsonp_hash_str, jsonp_str_equal, NULL, NULL)) {
|
||||
if(hashtable_init(&key_set)) {
|
||||
set_error(s, "<internal>", "Out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!json_is_object(root)) {
|
||||
if(root && !json_is_object(root)) {
|
||||
set_error(s, "<validation>", "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);
|
||||
(strict == 1 ? '!' : '*'), token(s));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!s->token) {
|
||||
if(!token(s)) {
|
||||
set_error(s, "<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>", "Expected format 's', got '%c'", token(s));
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -279,23 +395,34 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
next_token(s);
|
||||
|
||||
value = json_object_get(root, key);
|
||||
if(!value) {
|
||||
set_error(s, "<validation>", "Object item not found: %s", key);
|
||||
goto out;
|
||||
if(token(s) == '?') {
|
||||
opt = 1;
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
if(!root) {
|
||||
/* skipping */
|
||||
value = NULL;
|
||||
}
|
||||
else {
|
||||
value = json_object_get(root, key);
|
||||
if(!value && !opt) {
|
||||
set_error(s, "<validation>", "Object item not found: %s", key);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(unpack(s, value, ap))
|
||||
goto out;
|
||||
|
||||
hashtable_set(&key_set, (void *)key, NULL);
|
||||
hashtable_set(&key_set, key, 0, json_null());
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||
strict = 1;
|
||||
|
||||
if(strict == 1 && key_set.size != json_object_size(root)) {
|
||||
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;
|
||||
@@ -313,44 +440,50 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||
size_t i = 0;
|
||||
int strict = 0;
|
||||
|
||||
if(!json_is_array(root)) {
|
||||
if(root && !json_is_array(root)) {
|
||||
set_error(s, "<validation>", "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'",
|
||||
(strict == 1 ? '!' : '*'),
|
||||
s->token);
|
||||
token(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!s->token) {
|
||||
if(!token(s)) {
|
||||
set_error(s, "<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)) {
|
||||
if(!strchr(unpack_value_starters, token(s))) {
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
s->token);
|
||||
token(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = json_array_get(root, i);
|
||||
if(!value) {
|
||||
set_error(s, "<validation>", "Array index %lu out of range",
|
||||
(unsigned long)i);
|
||||
return -1;
|
||||
if(!root) {
|
||||
/* skipping */
|
||||
value = NULL;
|
||||
}
|
||||
else {
|
||||
value = json_array_get(root, i);
|
||||
if(!value) {
|
||||
set_error(s, "<validation>", "Array index %lu out of range",
|
||||
(unsigned long)i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(unpack(s, value, ap))
|
||||
@@ -363,7 +496,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||
strict = 1;
|
||||
|
||||
if(strict == 1 && i != json_array_size(root)) {
|
||||
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);
|
||||
return -1;
|
||||
@@ -374,7 +507,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);
|
||||
@@ -383,99 +516,118 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
return unpack_array(s, root, ap);
|
||||
|
||||
case 's':
|
||||
if(!json_is_string(root)) {
|
||||
if(root && !json_is_string(root)) {
|
||||
set_error(s, "<validation>", "Expected string, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
const char **str;
|
||||
const char **target;
|
||||
|
||||
str = va_arg(*ap, const char **);
|
||||
if(!str) {
|
||||
target = va_arg(*ap, const char **);
|
||||
if(!target) {
|
||||
set_error(s, "<args>", "NULL string argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*str = json_string_value(root);
|
||||
if(root)
|
||||
*target = json_string_value(root);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 'i':
|
||||
if(!json_is_integer(root)) {
|
||||
if(root && !json_is_integer(root)) {
|
||||
set_error(s, "<validation>", "Expected integer, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
*va_arg(*ap, int*) = json_integer_value(root);
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
int *target = va_arg(*ap, int*);
|
||||
if(root)
|
||||
*target = (int)json_integer_value(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'I':
|
||||
if(!json_is_integer(root)) {
|
||||
if(root && !json_is_integer(root)) {
|
||||
set_error(s, "<validation>", "Expected integer, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
*va_arg(*ap, json_int_t*) = json_integer_value(root);
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
json_int_t *target = va_arg(*ap, json_int_t*);
|
||||
if(root)
|
||||
*target = json_integer_value(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'b':
|
||||
if(!json_is_boolean(root)) {
|
||||
if(root && !json_is_boolean(root)) {
|
||||
set_error(s, "<validation>", "Expected true or false, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
*va_arg(*ap, int*) = json_is_true(root);
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
int *target = va_arg(*ap, int*);
|
||||
if(root)
|
||||
*target = json_is_true(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'f':
|
||||
if(!json_is_real(root)) {
|
||||
if(root && !json_is_real(root)) {
|
||||
set_error(s, "<validation>", "Expected real, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
*va_arg(*ap, double*) = json_real_value(root);
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
double *target = va_arg(*ap, double*);
|
||||
if(root)
|
||||
*target = json_real_value(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'F':
|
||||
if(!json_is_number(root)) {
|
||||
if(root && !json_is_number(root)) {
|
||||
set_error(s, "<validation>", "Expected real or integer, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
*va_arg(*ap, double*) = json_number_value(root);
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
double *target = va_arg(*ap, double*);
|
||||
if(root)
|
||||
*target = json_number_value(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'O':
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
if(root && !(s->flags & JSON_VALIDATE_ONLY))
|
||||
json_incref(root);
|
||||
/* Fall through */
|
||||
|
||||
case 'o':
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY))
|
||||
*va_arg(*ap, json_t**) = root;
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
json_t **target = va_arg(*ap, json_t**);
|
||||
if(root)
|
||||
*target = root;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'n':
|
||||
/* Never assign, just validate */
|
||||
if(!json_is_null(root)) {
|
||||
if(root && !json_is_null(root)) {
|
||||
set_error(s, "<validation>", "Expected null, got %s",
|
||||
type_name(root));
|
||||
return -1;
|
||||
@@ -484,7 +636,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
|
||||
default:
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
s->token);
|
||||
token(s));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -514,7 +666,7 @@ 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>", "Garbage after format string");
|
||||
return NULL;
|
||||
@@ -577,7 +729,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
|
||||
va_end(ap_copy);
|
||||
|
||||
next_token(&s);
|
||||
if(s.token) {
|
||||
if(token(&s)) {
|
||||
set_error(&s, "<format>", "Garbage after format string");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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"
|
||||
@@ -13,6 +16,7 @@
|
||||
|
||||
#define STRBUFFER_MIN_SIZE 16
|
||||
#define STRBUFFER_FACTOR 2
|
||||
#define STRBUFFER_SIZE_MAX ((size_t)-1)
|
||||
|
||||
int strbuffer_init(strbuffer_t *strbuff)
|
||||
{
|
||||
@@ -30,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;
|
||||
@@ -50,7 +56,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -64,13 +70,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
|
||||
return strbuffer_append_bytes(strbuff, &byte, 1);
|
||||
}
|
||||
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
|
||||
{
|
||||
if(strbuff->length + size >= strbuff->size)
|
||||
if(size >= strbuff->size - strbuff->length)
|
||||
{
|
||||
size_t new_size;
|
||||
char *new_value;
|
||||
|
||||
/* avoid integer overflow */
|
||||
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|
||||
|| size > STRBUFFER_SIZE_MAX - 1
|
||||
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
|
||||
return -1;
|
||||
|
||||
new_size = max(strbuff->size * STRBUFFER_FACTOR,
|
||||
strbuff->length + size + 1);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,8 +10,8 @@
|
||||
|
||||
typedef struct {
|
||||
char *value;
|
||||
int length; /* bytes used */
|
||||
int size; /* bytes allocated */
|
||||
size_t length; /* bytes used */
|
||||
size_t size; /* bytes allocated */
|
||||
} strbuffer_t;
|
||||
|
||||
int strbuffer_init(strbuffer_t *strbuff);
|
||||
@@ -20,11 +20,13 @@ 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, int size);
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
|
||||
|
||||
char strbuffer_pop(strbuffer_t *strbuff);
|
||||
|
||||
|
||||
134
src/strconv.c
Normal file
134
src/strconv.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
|
||||
/* need config.h to get the correct snprintf */
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#if JSON_HAVE_LOCALECONV
|
||||
#include <locale.h>
|
||||
|
||||
/*
|
||||
- This code assumes that the decimal separator is exactly one
|
||||
character.
|
||||
|
||||
- If setlocale() is called by another thread between the call to
|
||||
localeconv() and the call to sprintf() or strtod(), the result may
|
||||
be wrong. setlocale() is not thread-safe and should not be used
|
||||
this way. Multi-threaded programs should use uselocale() instead.
|
||||
*/
|
||||
|
||||
static void to_locale(strbuffer_t *strbuffer)
|
||||
{
|
||||
const char *point;
|
||||
char *pos;
|
||||
|
||||
point = localeconv()->decimal_point;
|
||||
if(*point == '.') {
|
||||
/* No conversion needed */
|
||||
return;
|
||||
}
|
||||
|
||||
pos = strchr(strbuffer->value, '.');
|
||||
if(pos)
|
||||
*pos = *point;
|
||||
}
|
||||
|
||||
static void from_locale(char *buffer)
|
||||
{
|
||||
const char *point;
|
||||
char *pos;
|
||||
|
||||
point = localeconv()->decimal_point;
|
||||
if(*point == '.') {
|
||||
/* No conversion needed */
|
||||
return;
|
||||
}
|
||||
|
||||
pos = strchr(buffer, *point);
|
||||
if(pos)
|
||||
*pos = '.';
|
||||
}
|
||||
#endif
|
||||
|
||||
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
{
|
||||
double value;
|
||||
char *end;
|
||||
|
||||
#if JSON_HAVE_LOCALECONV
|
||||
to_locale(strbuffer);
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
value = strtod(strbuffer->value, &end);
|
||||
assert(end == strbuffer->value + strbuffer->length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
/* Overflow */
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value)
|
||||
{
|
||||
int ret;
|
||||
char *start, *end;
|
||||
size_t length;
|
||||
|
||||
ret = snprintf(buffer, size, "%.17g", value);
|
||||
if(ret < 0)
|
||||
return -1;
|
||||
|
||||
length = (size_t)ret;
|
||||
if(length >= size)
|
||||
return -1;
|
||||
|
||||
#if JSON_HAVE_LOCALECONV
|
||||
from_locale(buffer);
|
||||
#endif
|
||||
|
||||
/* Make sure there's a dot or 'e' in the output. Otherwise
|
||||
a real is converted to an integer when decoding */
|
||||
if(strchr(buffer, '.') == NULL &&
|
||||
strchr(buffer, 'e') == NULL)
|
||||
{
|
||||
if(length + 3 >= size) {
|
||||
/* No space to append ".0" */
|
||||
return -1;
|
||||
}
|
||||
buffer[length] = '.';
|
||||
buffer[length + 1] = '0';
|
||||
buffer[length + 2] = '\0';
|
||||
length += 2;
|
||||
}
|
||||
|
||||
/* Remove leading '+' from positive exponent. Also remove leading
|
||||
zeros from exponents (added by some printf() implementations) */
|
||||
start = strchr(buffer, 'e');
|
||||
if(start) {
|
||||
start++;
|
||||
end = start + 1;
|
||||
|
||||
if(*start == '-')
|
||||
start++;
|
||||
|
||||
while(*end == '0')
|
||||
end++;
|
||||
|
||||
if(end != start) {
|
||||
memmove(start, end, length - (size_t)(end - buffer));
|
||||
length -= (size_t)(end - start);
|
||||
}
|
||||
}
|
||||
|
||||
return (int)length;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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.
|
||||
|
||||
22
src/utf.h
22
src/utf.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,23 +10,11 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <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 */
|
||||
|
||||
#else /* !HAVE_CONFIG_H */
|
||||
#ifdef _WIN32
|
||||
typedef int int32_t;
|
||||
#else /* !_WIN32 */
|
||||
/* Assume a standard environment */
|
||||
#include <inttypes.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
|
||||
|
||||
248
src/value.c
248
src/value.c
@@ -1,21 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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 <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
|
||||
static JSON_INLINE int isnan(double x) { return x != x; }
|
||||
#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,60 +44,22 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||
|
||||
/*** object ***/
|
||||
|
||||
/* From http://www.cse.yorku.ca/~oz/hash.html */
|
||||
size_t jsonp_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;
|
||||
}
|
||||
|
||||
int jsonp_str_equal(const void *ptr1, const void *ptr2)
|
||||
{
|
||||
return strcmp((const char *)ptr1, (const char *)ptr2) == 0;
|
||||
}
|
||||
|
||||
/* This macro just returns a pointer that's a few bytes backwards from
|
||||
string. This makes it possible to pass a pointer to object_key_t
|
||||
when only the string inside it is used, without actually creating
|
||||
an object_key_t instance. */
|
||||
#define string_to_key(string) container_of(string, object_key_t, key)
|
||||
|
||||
static size_t hash_key(const void *ptr)
|
||||
{
|
||||
return jsonp_hash_str(((const object_key_t *)ptr)->key);
|
||||
}
|
||||
|
||||
static int key_equal(const void *ptr1, const void *ptr2)
|
||||
{
|
||||
return jsonp_str_equal(((const object_key_t *)ptr1)->key,
|
||||
((const object_key_t *)ptr2)->key);
|
||||
}
|
||||
|
||||
static void value_decref(void *value)
|
||||
{
|
||||
json_decref((json_t *)value);
|
||||
}
|
||||
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,
|
||||
hash_key, key_equal,
|
||||
jsonp_free, value_decref))
|
||||
if(hashtable_init(&object->hashtable))
|
||||
{
|
||||
jsonp_free(object);
|
||||
return NULL;
|
||||
@@ -116,35 +96,24 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_get(&object->hashtable, string_to_key(key));
|
||||
return hashtable_get(&object->hashtable, key);
|
||||
}
|
||||
|
||||
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
json_object_t *object;
|
||||
object_key_t *k;
|
||||
|
||||
if(!key || !value)
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_object(json) || json == value)
|
||||
if(!key || !json_is_object(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
object = json_to_object(json);
|
||||
|
||||
/* offsetof(...) returns the size of object_key_t without the
|
||||
last, flexible member. This way, the correct amount is
|
||||
allocated. */
|
||||
k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
|
||||
if(!k)
|
||||
return -1;
|
||||
|
||||
k->serial = object->serial++;
|
||||
strcpy(k->key, key);
|
||||
|
||||
if(hashtable_set(&object->hashtable, k, value))
|
||||
if(hashtable_set(&object->hashtable, key, object->serial++, value))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@@ -172,7 +141,7 @@ int json_object_del(json_t *json, const char *key)
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_del(&object->hashtable, string_to_key(key));
|
||||
return hashtable_del(&object->hashtable, key);
|
||||
}
|
||||
|
||||
int json_object_clear(json_t *json)
|
||||
@@ -183,30 +152,56 @@ int json_object_clear(json_t *json)
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
|
||||
hashtable_clear(&object->hashtable);
|
||||
object->serial = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_update(json_t *object, json_t *other)
|
||||
{
|
||||
void *iter;
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
if(!json_is_object(object) || !json_is_object(other))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter(other);
|
||||
while(iter) {
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
|
||||
json_object_foreach(other, key, value) {
|
||||
if(json_object_set_nocheck(object, key, value))
|
||||
return -1;
|
||||
}
|
||||
|
||||
iter = json_object_iter_next(other, iter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_update_existing(json_t *object, json_t *other)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
if(!json_is_object(object) || !json_is_object(other))
|
||||
return -1;
|
||||
|
||||
json_object_foreach(other, key, value) {
|
||||
if(json_object_get(object, key))
|
||||
json_object_set_nocheck(object, key, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_update_missing(json_t *object, json_t *other)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
if(!json_is_object(object) || !json_is_object(other))
|
||||
return -1;
|
||||
|
||||
json_object_foreach(other, key, value) {
|
||||
if(!json_object_get(object, key))
|
||||
json_object_set_nocheck(object, key, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -231,7 +226,7 @@ void *json_object_iter_at(json_t *json, const char *key)
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_iter_at(&object->hashtable, string_to_key(key));
|
||||
return hashtable_iter_at(&object->hashtable, key);
|
||||
}
|
||||
|
||||
void *json_object_iter_next(json_t *json, void *iter)
|
||||
@@ -245,20 +240,12 @@ void *json_object_iter_next(json_t *json, void *iter)
|
||||
return hashtable_iter_next(&object->hashtable, iter);
|
||||
}
|
||||
|
||||
const object_key_t *jsonp_object_iter_fullkey(void *iter)
|
||||
{
|
||||
if(!iter)
|
||||
return NULL;
|
||||
|
||||
return hashtable_iter_key(iter);
|
||||
}
|
||||
|
||||
const char *json_object_iter_key(void *iter)
|
||||
{
|
||||
if(!iter)
|
||||
return NULL;
|
||||
|
||||
return jsonp_object_iter_fullkey(iter)->key;
|
||||
return hashtable_iter_key(iter);
|
||||
}
|
||||
|
||||
json_t *json_object_iter_value(void *iter)
|
||||
@@ -271,38 +258,34 @@ json_t *json_object_iter_value(void *iter)
|
||||
|
||||
int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json) || !iter || !value)
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
hashtable_iter_set(&object->hashtable, iter, value);
|
||||
|
||||
hashtable_iter_set(iter, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *json_object_key_to_iter(const char *key)
|
||||
{
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
return hashtable_key_to_iter(key);
|
||||
}
|
||||
|
||||
static int json_object_equal(json_t *object1, json_t *object2)
|
||||
{
|
||||
void *iter;
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
if(json_object_size(object1) != json_object_size(object2))
|
||||
return 0;
|
||||
|
||||
iter = json_object_iter(object1);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value1 = json_object_iter_value(iter);
|
||||
json_object_foreach(object1, key, value1) {
|
||||
value2 = json_object_get(object2, key);
|
||||
|
||||
if(!json_equal(value1, value2))
|
||||
return 0;
|
||||
|
||||
iter = json_object_iter_next(object1, iter);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -311,29 +294,21 @@ static int json_object_equal(json_t *object1, json_t *object2)
|
||||
static json_t *json_object_copy(json_t *object)
|
||||
{
|
||||
json_t *result;
|
||||
void *iter;
|
||||
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
result = json_object();
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
json_object_foreach(object, key, value)
|
||||
json_object_set_nocheck(result, key, value);
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
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;
|
||||
void *iter;
|
||||
@@ -342,17 +317,17 @@ static json_t *json_object_deep_copy(json_t *object)
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
/* 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;
|
||||
json_t *value;
|
||||
|
||||
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(object, iter);
|
||||
json_object_set_new_nocheck(result, key, json_deep_copy(value));
|
||||
iter = json_object_iter_next((json_t *)object, iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -560,7 +535,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;
|
||||
@@ -641,7 +619,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;
|
||||
@@ -700,6 +678,9 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
|
||||
if(!json_is_string(json) || !value)
|
||||
return -1;
|
||||
|
||||
dup = jsonp_strdup(value);
|
||||
if(!dup)
|
||||
return -1;
|
||||
@@ -730,7 +711,7 @@ static int json_string_equal(json_t *string1, json_t *string2)
|
||||
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -777,7 +758,7 @@ static int json_integer_equal(json_t *integer1, 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));
|
||||
}
|
||||
@@ -787,7 +768,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);
|
||||
@@ -806,8 +792,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;
|
||||
|
||||
@@ -824,7 +810,7 @@ static int json_real_equal(json_t *real1, 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));
|
||||
}
|
||||
@@ -835,7 +821,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
|
||||
@@ -950,7 +936,7 @@ json_t *json_copy(json_t *json)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_t *json_deep_copy(json_t *json)
|
||||
json_t *json_deep_copy(const json_t *json)
|
||||
{
|
||||
if(!json)
|
||||
return NULL;
|
||||
@@ -974,7 +960,7 @@ json_t *json_deep_copy(json_t *json)
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return json;
|
||||
return (json_t *)json;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
3
test/.gitignore
vendored
3
test/.gitignore
vendored
@@ -4,11 +4,14 @@ suites/api/test_array
|
||||
suites/api/test_copy
|
||||
suites/api/test_cpp
|
||||
suites/api/test_dump
|
||||
suites/api/test_dump_callback
|
||||
suites/api/test_equal
|
||||
suites/api/test_load
|
||||
suites/api/test_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_unpack
|
||||
suites/api/test_load_callback
|
||||
|
||||
@@ -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,16 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#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;
|
||||
} 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, "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 > 255) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent);
|
||||
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.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;
|
||||
@@ -27,38 +243,19 @@ 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 && isspace(*result))
|
||||
result++;
|
||||
|
||||
length = strlen(result);
|
||||
if(length == 0)
|
||||
return result;
|
||||
|
||||
while(isspace(result[length - 1]))
|
||||
result[--length] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int indent = 0;
|
||||
int indent;
|
||||
size_t flags = 0;
|
||||
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
/* On Windows, set stdout and stderr to binary mode to avoid
|
||||
outputting DOS line terminators */
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
_setmode(_fileno(stderr), _O_BINARY);
|
||||
#endif
|
||||
|
||||
indent = getenv_int("JSON_INDENT");
|
||||
if(indent < 0 || indent > 255) {
|
||||
@@ -81,13 +278,16 @@ int main(int argc, char *argv[])
|
||||
if(getenv_int("JSON_SORT_KEYS"))
|
||||
flags |= JSON_SORT_KEYS;
|
||||
|
||||
if(getenv("HASHSEED"))
|
||||
json_object_seed(getenv_int("HASHSEED"));
|
||||
|
||||
if(getenv_int("STRIP")) {
|
||||
/* Load to memory, strip leading and trailing whitespace */
|
||||
size_t size = 0, used = 0;
|
||||
char *buffer = NULL;
|
||||
|
||||
while(1) {
|
||||
int count;
|
||||
size_t count;
|
||||
|
||||
size = (size == 0 ? 128 : size * 2);
|
||||
buffer = realloc(buffer, size);
|
||||
@@ -112,8 +312,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;
|
||||
}
|
||||
|
||||
@@ -122,3 +322,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;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ fi
|
||||
|
||||
[ -z "$STOP" ] && STOP=0
|
||||
|
||||
export suites_srcdir=$top_srcdir/test/suites
|
||||
export suites_builddir=suites
|
||||
export scriptdir=$top_srcdir/test/scripts
|
||||
export logdir=logs
|
||||
export bindir=bin
|
||||
suites_srcdir=$top_srcdir/test/suites
|
||||
suites_builddir=suites
|
||||
scriptdir=$top_srcdir/test/scripts
|
||||
logdir=logs
|
||||
bindir=bin
|
||||
export suites_srcdir suites_builddir scriptdir logdir bindir
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 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-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 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
|
||||
@@ -1,11 +1,14 @@
|
||||
EXTRA_DIST = run
|
||||
EXTRA_DIST = run check-exports
|
||||
|
||||
check_PROGRAMS = \
|
||||
test_array \
|
||||
test_copy \
|
||||
test_dump \
|
||||
test_dump_callback \
|
||||
test_equal \
|
||||
test_load \
|
||||
test_loadb \
|
||||
test_load_callback \
|
||||
test_memory_funcs \
|
||||
test_number \
|
||||
test_object \
|
||||
@@ -16,7 +19,9 @@ check_PROGRAMS = \
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
test_copy_SOURCES = test_copy.c util.h
|
||||
test_dump_SOURCES = test_dump.c util.h
|
||||
test_dump_callback_SOURCES = test_dump_callback.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_loadb_SOURCES = test_loadb.c util.h
|
||||
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_object.c util.h
|
||||
@@ -24,7 +29,6 @@ test_pack_SOURCES = test_pack.c util.h
|
||||
test_simple_SOURCES = test_simple.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,98 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This tests checks that the 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_iter
|
||||
json_object_iter_at
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
json_loads
|
||||
json_loadf
|
||||
json_load_file
|
||||
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
|
||||
#
|
||||
# This test checks that libjansson.so exports the correct symbols.
|
||||
#
|
||||
|
||||
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-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 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-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,8 +400,27 @@ static void test_circular()
|
||||
json_decref(array1);
|
||||
}
|
||||
|
||||
static void test_array_foreach()
|
||||
{
|
||||
size_t index;
|
||||
json_t *array1, *array2, *value;
|
||||
|
||||
int main()
|
||||
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()
|
||||
{
|
||||
test_misc();
|
||||
test_insert();
|
||||
@@ -395,6 +428,5 @@ int main()
|
||||
test_clear();
|
||||
test_extend();
|
||||
test_circular();
|
||||
|
||||
return 0;
|
||||
test_array_foreach();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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.
|
||||
@@ -307,7 +307,7 @@ static void test_deep_copy_object(void)
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_copy_simple();
|
||||
test_deep_copy_simple();
|
||||
@@ -315,5 +315,4 @@ int main()
|
||||
test_deep_copy_array();
|
||||
test_copy_object();
|
||||
test_deep_copy_object();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,13 +9,36 @@
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
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_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1)
|
||||
fail("json_dumpf didn't fail for NULL");
|
||||
|
||||
/* 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 */
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json = json_object();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{}"))
|
||||
@@ -43,7 +66,10 @@ int main()
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void circular_references()
|
||||
{
|
||||
/* Construct a JSON object/array with a circular reference:
|
||||
|
||||
object: {"a": {"b": {"c": <circular reference to $.a>}}}
|
||||
@@ -51,6 +77,10 @@ int main()
|
||||
|
||||
Encode it, remove the circular reference and encode again.
|
||||
*/
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_object();
|
||||
json_object_set_new(json, "a", json_object());
|
||||
json_object_set_new(json_object_get(json, "a"), "b", json_object());
|
||||
@@ -86,6 +116,75 @@ int main()
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void encode_other_than_array_or_object()
|
||||
{
|
||||
/* Encoding anything other than array or object should only
|
||||
* succeed if the JSON_ENCODE_ANY flag is used */
|
||||
|
||||
json_t *json;
|
||||
FILE *fp = NULL;
|
||||
char *result;
|
||||
|
||||
json = json_string("foo");
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded a string!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded a string!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "\"foo\"") != 0)
|
||||
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
json = json_integer(42);
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded an integer!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded an integer!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "42") != 0)
|
||||
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void escape_slashes()
|
||||
{
|
||||
/* 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 run_tests()
|
||||
{
|
||||
encode_null();
|
||||
encode_twice();
|
||||
circular_references();
|
||||
encode_other_than_array_or_object();
|
||||
escape_slashes();
|
||||
}
|
||||
|
||||
81
test/suites/api/test_dump_callback.c
Normal file
81
test/suites/api/test_dump_callback.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 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_sink {
|
||||
char *buf;
|
||||
size_t off;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
static int my_writer(const char *buffer, size_t len, void *data) {
|
||||
struct my_sink *s = data;
|
||||
if (len > s->cap - s->off) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->off, buffer, len);
|
||||
s->off += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
struct my_sink s;
|
||||
json_t *json;
|
||||
const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
|
||||
char *dumped_to_string;
|
||||
|
||||
json = json_loads(str, 0, NULL);
|
||||
if(!json) {
|
||||
fail("json_loads failed");
|
||||
}
|
||||
|
||||
dumped_to_string = json_dumps(json, 0);
|
||||
if (!dumped_to_string) {
|
||||
json_decref(json);
|
||||
fail("json_dumps failed");
|
||||
}
|
||||
|
||||
s.off = 0;
|
||||
s.cap = strlen(dumped_to_string);
|
||||
s.buf = malloc(s.cap);
|
||||
if (!s.buf) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
fail("malloc failed");
|
||||
}
|
||||
|
||||
if (json_dump_callback(json, my_writer, &s, 0) == -1) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
fail("json_dump_callback failed on an exact-length sink buffer");
|
||||
}
|
||||
|
||||
if (strncmp(dumped_to_string, s.buf, s.off) != 0) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
fail("json_dump_callback and json_dumps did not produce identical output");
|
||||
}
|
||||
|
||||
s.off = 1;
|
||||
if (json_dump_callback(json, my_writer, &s, 0) != -1) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
fail("json_dump_callback succeeded on a short buffer when it should have failed");
|
||||
}
|
||||
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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.
|
||||
@@ -180,11 +180,10 @@ static void test_equal_complex()
|
||||
/* TODO: There's no negative test case here */
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_equal_simple();
|
||||
test_equal_array();
|
||||
test_equal_object();
|
||||
test_equal_complex();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,16 +9,158 @@
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void file_not_found()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
char *pos;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
|
||||
if(json)
|
||||
fail("json_load_file returned non-NULL for a nonexistent file");
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
|
||||
/* The error message is locale specific, only check the beginning
|
||||
of the error message. */
|
||||
|
||||
pos = strchr(error.text, ':');
|
||||
if(!pos)
|
||||
fail("json_load_file returne an invalid error message");
|
||||
|
||||
*pos = '\0';
|
||||
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
{
|
||||
json_error_t error;
|
||||
|
||||
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
|
||||
fail("json_loads did not detect a duplicate key");
|
||||
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
}
|
||||
|
||||
static void disable_eof_check()
|
||||
{
|
||||
json_error_t error;
|
||||
json_t *json;
|
||||
|
||||
const char *text = "{\"foo\": 1} garbage";
|
||||
|
||||
if(json_loads(text, 0, &error))
|
||||
fail("json_loads did not detect garbage after JSON text");
|
||||
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
|
||||
|
||||
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
|
||||
if(!json)
|
||||
fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void decode_any()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
json = json_loads("\"foo\"", JSON_DECODE_ANY, &error);
|
||||
if (!json || !json_is_string(json))
|
||||
fail("json_load decoded any failed - string");
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
json = json_loads("42", JSON_DECODE_ANY, &error);
|
||||
if (!json || !json_is_integer(json))
|
||||
fail("json_load decoded any failed - integer");
|
||||
json_decref(json);
|
||||
|
||||
json = json_loads("true", JSON_DECODE_ANY, &error);
|
||||
if (!json || !json_is_true(json))
|
||||
fail("json_load decoded any failed - boolean");
|
||||
json_decref(json);
|
||||
|
||||
json = json_loads("null", JSON_DECODE_ANY, &error);
|
||||
if (!json || !json_is_null(json))
|
||||
fail("json_load decoded any failed - null");
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
static void load_wrong_args()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_loads(NULL, 0, &error);
|
||||
if (json)
|
||||
fail("json_loads should return NULL if the first argument is NULL");
|
||||
|
||||
json = json_loadb(NULL, 0, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadb should return NULL if the first argument is NULL");
|
||||
|
||||
json = json_loadf(NULL, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadf should return NULL if the first argument is NULL");
|
||||
|
||||
json = json_load_file(NULL, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadf should return NULL if the first argument is NULL");
|
||||
}
|
||||
|
||||
static void position()
|
||||
{
|
||||
json_t *json;
|
||||
size_t flags = JSON_DISABLE_EOF_CHECK;
|
||||
json_error_t error;
|
||||
|
||||
json = json_loads("{\"foo\": \"bar\"}", 0, &error);
|
||||
if(error.position != 14)
|
||||
fail("json_loads returned a wrong position");
|
||||
json_decref(json);
|
||||
|
||||
json = json_loads("{\"foo\": \"bar\"} baz quux", flags, &error);
|
||||
if(error.position != 14)
|
||||
fail("json_loads returned a wrong position");
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
file_not_found();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
decode_any();
|
||||
decode_int_as_real();
|
||||
load_wrong_args();
|
||||
position();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
36
test/suites/api/test_loadb.c
Normal file
36
test/suites/api/test_loadb.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 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 "util.h"
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage";
|
||||
size_t len = strlen(str) - strlen("garbage");
|
||||
|
||||
json = json_loadb(str, len, 0, &error);
|
||||
if(!json) {
|
||||
fail("json_loadb failed on a valid JSON buffer");
|
||||
}
|
||||
json_decref(json);
|
||||
|
||||
json = json_loadb(str, len - 1, 0, &error);
|
||||
if (json) {
|
||||
json_decref(json);
|
||||
fail("json_loadb should have failed on an incomplete buffer, but it didn't");
|
||||
}
|
||||
if(error.line != 1) {
|
||||
fail("json_loadb returned an invalid line number on fail");
|
||||
}
|
||||
if(strcmp(error.text, "']' expected near end of file") != 0) {
|
||||
fail("json_loadb returned an invalid error message for an unclosed top-level array");
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,13 @@ static void create_and_free_complex_object()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ static void test_simple()
|
||||
json_set_alloc_funcs(my_malloc, my_free);
|
||||
create_and_free_complex_object();
|
||||
|
||||
if(malloc_called != 27 || free_called != 27)
|
||||
if(malloc_called != 1 || free_called != 1)
|
||||
fail("Custom allocation failed");
|
||||
}
|
||||
|
||||
@@ -55,17 +55,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);
|
||||
}
|
||||
|
||||
@@ -75,10 +75,8 @@ static void test_secure_funcs(void)
|
||||
create_and_free_complex_object();
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_simple();
|
||||
test_secure_funcs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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"
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *integer, *real;
|
||||
int i;
|
||||
json_int_t i;
|
||||
double d;
|
||||
|
||||
integer = json_integer(5);
|
||||
@@ -40,5 +41,33 @@ int main()
|
||||
json_decref(integer);
|
||||
json_decref(real);
|
||||
|
||||
return 0;
|
||||
#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
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,48 @@ static void test_update()
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_conditional_updates()
|
||||
{
|
||||
json_t *object, *other;
|
||||
|
||||
object = json_pack("{sisi}", "foo", 1, "bar", 2);
|
||||
other = json_pack("{sisi}", "foo", 3, "baz", 4);
|
||||
|
||||
if(json_object_update_existing(object, other))
|
||||
fail("json_object_update_existing failed");
|
||||
|
||||
if(json_object_size(object) != 2)
|
||||
fail("json_object_update_existing added new items");
|
||||
|
||||
if(json_integer_value(json_object_get(object, "foo")) != 3)
|
||||
fail("json_object_update_existing failed to update existing key");
|
||||
|
||||
if(json_integer_value(json_object_get(object, "bar")) != 2)
|
||||
fail("json_object_update_existing updated wrong key");
|
||||
|
||||
json_decref(object);
|
||||
|
||||
object = json_pack("{sisi}", "foo", 1, "bar", 2);
|
||||
|
||||
if(json_object_update_missing(object, other))
|
||||
fail("json_object_update_missing failed");
|
||||
|
||||
if(json_object_size(object) != 3)
|
||||
fail("json_object_update_missing didn't add new items");
|
||||
|
||||
if(json_integer_value(json_object_get(object, "foo")) != 1)
|
||||
fail("json_object_update_missing updated existing key");
|
||||
|
||||
if(json_integer_value(json_object_get(object, "bar")) != 2)
|
||||
fail("json_object_update_missing updated wrong key");
|
||||
|
||||
if(json_integer_value(json_object_get(object, "baz")) != 4)
|
||||
fail("json_object_update_missing didn't add new items");
|
||||
|
||||
json_decref(object);
|
||||
json_decref(other);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *object1, *object2;
|
||||
@@ -207,7 +249,11 @@ static void test_set_nocheck()
|
||||
|
||||
static void test_iterators()
|
||||
{
|
||||
int i;
|
||||
json_t *object, *foo, *bar, *baz;
|
||||
const char *iter_keys[3];
|
||||
int have_key[3] = { 0, 0, 0 };
|
||||
json_t *iter_values[3];
|
||||
void *iter;
|
||||
|
||||
if(json_object_iter(NULL))
|
||||
@@ -234,30 +280,50 @@ 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");
|
||||
iter_keys[0] = json_object_iter_key(iter);
|
||||
iter_values[0] = json_object_iter_value(iter);
|
||||
|
||||
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");
|
||||
iter_keys[1] = json_object_iter_key(iter);
|
||||
iter_values[1] = json_object_iter_value(iter);
|
||||
|
||||
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");
|
||||
iter_keys[2] = json_object_iter_key(iter);
|
||||
iter_values[2] = json_object_iter_value(iter);
|
||||
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
fail("able to iterate over the end");
|
||||
|
||||
/* Check that keys have correct values */
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (strcmp(iter_keys[i], "a") == 0) {
|
||||
if (iter_values[i] != foo)
|
||||
fail("wrong value for iter key a");
|
||||
else
|
||||
have_key[0] = 1;
|
||||
} else if (strcmp(iter_keys[i], "b") == 0) {
|
||||
if (iter_values[i] != bar)
|
||||
fail("wrong value for iter key b");
|
||||
else
|
||||
have_key[1] = 1;
|
||||
} else if (strcmp(iter_keys[i], "c") == 0) {
|
||||
if (iter_values[i] != baz)
|
||||
fail("wrong value for iter key c");
|
||||
else
|
||||
have_key[2] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that we got all keys */
|
||||
for(i = 0; i < 3; i++) {
|
||||
if(!have_key[i])
|
||||
fail("a key wasn't iterated over");
|
||||
}
|
||||
|
||||
if(json_object_iter_at(object, "foo"))
|
||||
fail("json_object_iter_at() succeeds for non-existent key");
|
||||
|
||||
@@ -270,22 +336,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);
|
||||
@@ -437,15 +495,33 @@ static void test_preserve_order()
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
int main()
|
||||
static void test_object_foreach()
|
||||
{
|
||||
const char *key;
|
||||
json_t *object1, *object2, *value;
|
||||
|
||||
object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
|
||||
object2 = json_object();
|
||||
|
||||
json_object_foreach(object1, key, value)
|
||||
json_object_set(object2, key, value);
|
||||
|
||||
if(!json_equal(object1, object2))
|
||||
fail("json_object_foreach failed to iterate all key-value pairs");
|
||||
|
||||
json_decref(object1);
|
||||
json_decref(object2);
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
test_update();
|
||||
test_conditional_updates();
|
||||
test_circular();
|
||||
test_set_nocheck();
|
||||
test_iterators();
|
||||
test_preserve_order();
|
||||
|
||||
return 0;
|
||||
test_object_foreach();
|
||||
}
|
||||
|
||||
@@ -1,31 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
* Copyright (c) 2009-2013 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <jansson_config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
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 @@ int main()
|
||||
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 @@ int main()
|
||||
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 @@ int main()
|
||||
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 @@ int main()
|
||||
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 @@ int main()
|
||||
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,47 @@ int main()
|
||||
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);
|
||||
|
||||
/* string and length */
|
||||
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, 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 failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length 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 */
|
||||
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 failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string concatenation and length 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 +127,7 @@ int main()
|
||||
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,7 +135,7 @@ int main()
|
||||
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);
|
||||
|
||||
@@ -105,7 +143,7 @@ int main()
|
||||
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);
|
||||
@@ -116,7 +154,17 @@ int main()
|
||||
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);
|
||||
|
||||
@@ -193,6 +241,11 @@ int main()
|
||||
fail("json_pack failed to catch null argument string");
|
||||
check_error("NULL string argument", "<args>", 1, 1, 1);
|
||||
|
||||
/* + on its own */
|
||||
if(json_pack_ex(&error, 0, "+", NULL))
|
||||
fail("json_pack failed to a lone +");
|
||||
check_error("Unexpected format character '+'", "<format>", 1, 1, 1);
|
||||
|
||||
/* NULL format */
|
||||
if(json_pack_ex(&error, 0, NULL))
|
||||
fail("json_pack failed to catch NULL format string");
|
||||
@@ -201,7 +254,7 @@ int main()
|
||||
/* 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("NULL string argument", "<args>", 1, 2, 2);
|
||||
|
||||
/* More complicated checks for row/columns */
|
||||
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
|
||||
@@ -221,12 +274,10 @@ int main()
|
||||
/* 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("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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,10 +10,26 @@
|
||||
#include "util.h"
|
||||
|
||||
/* Call the simple functions not covered by other tests of the public API */
|
||||
int main()
|
||||
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");
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_integer(1);
|
||||
if(json_typeof(value) != JSON_INTEGER)
|
||||
fail("json_typeof failed");
|
||||
@@ -180,6 +196,4 @@ int main()
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
* Copyright (c) 2009-2013 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.
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *j, *j2;
|
||||
int i1, i2, i3;
|
||||
@@ -96,14 +96,14 @@ int main()
|
||||
/* 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);
|
||||
@@ -337,5 +337,37 @@ int main()
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
return 0;
|
||||
/* Optional values */
|
||||
j = json_object();
|
||||
i1 = 0;
|
||||
if(json_unpack(j, "{s?i}", "foo", &i1))
|
||||
fail("json_unpack failed for optional key");
|
||||
if(i1 != 0)
|
||||
fail("json_unpack unpacked an optional key");
|
||||
json_decref(j);
|
||||
|
||||
i1 = 0;
|
||||
j = json_pack("{si}", "foo", 42);
|
||||
if(json_unpack(j, "{s?i}", "foo", &i1))
|
||||
fail("json_unpack failed for an optional value");
|
||||
if(i1 != 42)
|
||||
fail("json_unpack failed to unpack an optional value");
|
||||
json_decref(j);
|
||||
|
||||
j = json_object();
|
||||
i1 = i2 = i3 = 0;
|
||||
if(json_unpack(j, "{s?[ii]s?{s{si}}}",
|
||||
"foo", &i1, &i2,
|
||||
"bar", "baz", "quux", &i3))
|
||||
fail("json_unpack failed for complex optional values");
|
||||
if(i1 != 0 || i2 != 0 || i3 != 0)
|
||||
fail("json_unpack unexpectedly unpacked something");
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{s{si}}", "foo", "bar", 42);
|
||||
if(json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1))
|
||||
fail("json_unpack failed for complex optional values");
|
||||
if(i1 != 42)
|
||||
fail("json_unpack failed to unpack");
|
||||
json_decref(j);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2013 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,8 +8,16 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#if HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
|
||||
@@ -52,4 +60,15 @@
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
static void run_tests();
|
||||
|
||||
int main() {
|
||||
#ifdef HAVE_SETLOCALE
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
run_tests();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export JSON_COMPACT=1
|
||||
JSON_COMPACT=1
|
||||
export JSON_COMPACT
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export JSON_COMPACT=1
|
||||
JSON_COMPACT=1
|
||||
HASHSEED=1
|
||||
export JSON_COMPACT HASHSEED
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export JSON_ENSURE_ASCII=1
|
||||
JSON_ENSURE_ASCII=1
|
||||
export JSON_ENSURE_ASCII
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export JSON_INDENT=4
|
||||
JSON_INDENT=4
|
||||
export JSON_INDENT
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export JSON_INDENT=4
|
||||
export JSON_COMPACT=1
|
||||
JSON_INDENT=4
|
||||
JSON_COMPACT=1
|
||||
export JSON_INDENT JSON_COMPACT
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export JSON_INDENT=4
|
||||
export JSON_COMPACT=1
|
||||
JSON_INDENT=4
|
||||
JSON_COMPACT=1
|
||||
HASHSEED=1
|
||||
export JSON_INDENT JSON_COMPACT HASHSEED
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export JSON_INDENT=4
|
||||
JSON_INDENT=4
|
||||
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
|
||||
@@ -1 +1,2 @@
|
||||
export JSON_PRESERVE_ORDER=1
|
||||
JSON_PRESERVE_ORDER=1
|
||||
export JSON_PRESERVE_ORDER
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 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 +1,2 @@
|
||||
export JSON_SORT_KEYS=1
|
||||
JSON_SORT_KEYS=1
|
||||
export JSON_SORT_KEYS
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 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,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -20,7 +20,7 @@ do_run() {
|
||||
strip=1
|
||||
fi
|
||||
|
||||
STRIP=$strip $json_process \
|
||||
STRIP=$strip $json_process --env \
|
||||
<$test_path/input >$test_log/stdout$s 2>$test_log/stderr$s
|
||||
valgrind_check $test_log/stderr$s || return 1
|
||||
|
||||
|
||||
1
test/suites/valid/complex-array/env
Normal file
1
test/suites/valid/complex-array/env
Normal file
@@ -0,0 +1 @@
|
||||
JSON_SORT_KEYS=1
|
||||
@@ -1 +1 @@
|
||||
[1e+22]
|
||||
[1e22]
|
||||
@@ -1 +1 @@
|
||||
[1.2299999999999999e+47]
|
||||
[1.2299999999999999e47]
|
||||
@@ -1 +1 @@
|
||||
[1.23456e+80]
|
||||
[1.23456e80]
|
||||
@@ -1,11 +1,12 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2013 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.
|
||||
|
||||
export JSON_SORT_KEYS=1
|
||||
JSON_SORT_KEYS=1
|
||||
export JSON_SORT_KEYS
|
||||
|
||||
is_test() {
|
||||
test -d $test_path
|
||||
@@ -18,7 +19,7 @@ do_run() {
|
||||
strip=0
|
||||
[ "$variant" = "strip" ] && strip=1
|
||||
|
||||
STRIP=$strip $json_process \
|
||||
STRIP=$strip $json_process --env \
|
||||
<$test_path/input >$test_log/stdout$s 2>$test_log/stderr$s
|
||||
valgrind_check $test_log/stderr$s || return 1
|
||||
|
||||
|
||||
39
win32/jansson_config.h
Normal file
39
win32/jansson_config.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 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 configure script copies this file to jansson_config.h and
|
||||
* replaces @var@ substitutions by values that fit your system. If you
|
||||
* cannot run the configure script, you can do the value substitution
|
||||
* by hand.
|
||||
*/
|
||||
|
||||
#ifndef JANSSON_CONFIG_H
|
||||
#define JANSSON_CONFIG_H
|
||||
|
||||
/* 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 __inline
|
||||
#endif
|
||||
|
||||
/* If your compiler supports the `long long` type and the strtoll()
|
||||
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_INTEGER_IS_LONG_LONG 1
|
||||
|
||||
/* If locale.h and localeconv() are available, define to 1,
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV 1
|
||||
|
||||
#endif
|
||||
20
win32/vs2010/jansson.sln
Normal file
20
win32/vs2010/jansson.sln
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual C++ Express 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jansson", "jansson.vcxproj", "{76226D20-1972-4789-A595-EDACC7A76DC3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
108
win32/vs2010/jansson.vcxproj
Normal file
108
win32/vs2010/jansson.vcxproj
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\dump.c" />
|
||||
<ClCompile Include="..\..\src\error.c" />
|
||||
<ClCompile Include="..\..\src\hashtable.c" />
|
||||
<ClCompile Include="..\..\src\load.c" />
|
||||
<ClCompile Include="..\..\src\memory.c" />
|
||||
<ClCompile Include="..\..\src\pack_unpack.c" />
|
||||
<ClCompile Include="..\..\src\strbuffer.c" />
|
||||
<ClCompile Include="..\..\src\strconv.c" />
|
||||
<ClCompile Include="..\..\src\utf.c" />
|
||||
<ClCompile Include="..\..\src\value.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\hashtable.h" />
|
||||
<ClInclude Include="..\..\src\jansson.h" />
|
||||
<ClInclude Include="..\..\src\jansson_private.h" />
|
||||
<ClInclude Include="..\..\src\strbuffer.h" />
|
||||
<ClInclude Include="..\..\src\utf.h" />
|
||||
<ClInclude Include="..\jansson_config.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{76226D20-1972-4789-A595-EDACC7A76DC3}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>jansson_dll</RootNamespace>
|
||||
<ProjectName>jansson</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>Output\$(Configuration)\</OutDir>
|
||||
<IntDir>Build\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>Output\$(Configuration)\</OutDir>
|
||||
<IntDir>Build\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>../../src/jansson.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<ModuleDefinitionFile>../../src/jansson.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
69
win32/vs2010/jansson.vcxproj.filters
Normal file
69
win32/vs2010/jansson.vcxproj.filters
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\dump.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\error.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\hashtable.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\load.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\memory.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\pack_unpack.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\strbuffer.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\strconv.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\utf.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\value.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\hashtable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\jansson.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\jansson_private.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\strbuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\utf.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\jansson_config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
3
win32/vs2010/jansson.vcxproj.user
Normal file
3
win32/vs2010/jansson.vcxproj.user
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
</Project>
|
||||
Reference in New Issue
Block a user