Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b23201bb1a | ||
|
|
df454e3cf0 | ||
|
|
b8bb078cc2 | ||
|
|
3c51112063 | ||
|
|
1672bb5a65 | ||
|
|
b900967f6f | ||
|
|
746c2c3a99 | ||
|
|
2af820fb99 | ||
|
|
bc5741fb1a | ||
|
|
575f951b3e | ||
|
|
0cac862bbc | ||
|
|
4467bf243f | ||
|
|
ddd1e1f223 | ||
|
|
d1e97737d6 | ||
|
|
98be7da3e2 | ||
|
|
08cb7b6d6f | ||
|
|
b02db47881 | ||
|
|
074bb3838f | ||
|
|
3ba3b23fdc | ||
|
|
e9fcab08fb | ||
|
|
bdaf7584db | ||
|
|
889280c976 | ||
|
|
f9e7aa5eeb | ||
|
|
9258671924 | ||
|
|
a2bbb44d96 | ||
|
|
16b516f976 | ||
|
|
86196250b8 | ||
|
|
ada5372cff | ||
|
|
f11c1b9466 | ||
|
|
811a30691e | ||
|
|
7d1af52ab4 | ||
|
|
63b9fd0552 | ||
|
|
b45745118d | ||
|
|
0ffecdbade | ||
|
|
ab1ba69027 | ||
|
|
a5610c8895 | ||
|
|
012c5f0eca | ||
|
|
a931aace16 | ||
|
|
14573dc920 | ||
|
|
71594af7d5 | ||
|
|
6a4b3f878d | ||
|
|
86fdf76f79 | ||
|
|
835290dfdf | ||
|
|
7daffabf07 | ||
|
|
9df267054f | ||
|
|
8f067962f6 | ||
|
|
603fdc9154 | ||
|
|
520340998f | ||
|
|
2d710d832f | ||
|
|
a8c834c882 | ||
|
|
7438cc8ba8 | ||
|
|
72fd2fec4c | ||
|
|
762b299e71 | ||
|
|
013c3892c3 | ||
|
|
64ce0ad373 | ||
|
|
087ed94c45 | ||
|
|
006638a6a2 | ||
|
|
130c2fff31 | ||
|
|
cfd817895a | ||
|
|
ac97e0bf79 | ||
|
|
1927eeb4b4 | ||
|
|
c8361441fe | ||
|
|
107cfe9499 | ||
|
|
811965b475 | ||
|
|
e08101704c | ||
|
|
c17be5870b | ||
|
|
9d71d006cc | ||
|
|
b698ca13de | ||
|
|
0f50bb10b6 | ||
|
|
e6b60da043 | ||
|
|
89f4c15e6d | ||
|
|
1c2e707b34 | ||
|
|
52015cf35c | ||
|
|
1d513d063a | ||
|
|
581d5b899c | ||
|
|
4f49c07781 | ||
|
|
245e532934 | ||
|
|
e44b2231b5 | ||
|
|
40bd71f064 | ||
|
|
067dc50efe | ||
|
|
4876bda857 | ||
|
|
e89538f685 | ||
|
|
f7331c7194 | ||
|
|
02464862ed | ||
|
|
db0213ae56 | ||
|
|
8f2298bad8 | ||
|
|
4c4f692bd6 | ||
|
|
875b78dc97 | ||
|
|
fef27e6d3e | ||
|
|
1dab656dee | ||
|
|
69678aaa35 | ||
|
|
d384acd706 | ||
|
|
5d42e1520a | ||
|
|
d8753db4ac | ||
|
|
95dd927857 | ||
|
|
76760011ff | ||
|
|
11813f4128 | ||
|
|
8b1bdcacb7 | ||
|
|
c242b46016 | ||
|
|
58c188e1d5 | ||
|
|
ffc18128f4 | ||
|
|
6a38d0d431 | ||
|
|
d0a8ad4c06 | ||
|
|
c244b1483e | ||
|
|
fc83f10c85 | ||
|
|
8d561cd94e | ||
|
|
970c6988a5 | ||
|
|
220dcb7be3 | ||
|
|
48e0488f07 | ||
|
|
890760b2fb | ||
|
|
15653c47dd | ||
|
|
5508ab403d | ||
|
|
d799ee11b4 | ||
|
|
abaae7630e | ||
|
|
5c1d87592a | ||
|
|
5885035f5f | ||
|
|
dee4a7c29e | ||
|
|
82a55ef205 | ||
|
|
19f33c0e71 | ||
|
|
3c9e5c9925 | ||
|
|
1c38ab17f5 | ||
|
|
d5edfcc6fd | ||
|
|
9b435df3d4 | ||
|
|
bc743ad2d9 | ||
|
|
19cc800ad3 | ||
|
|
b52e7a69aa | ||
|
|
1395e4303a | ||
|
|
d7a6269a17 | ||
|
|
7fbe7c3960 | ||
|
|
ee27b7e3dd | ||
|
|
391d9101a8 | ||
|
|
2137e0c895 | ||
|
|
5fc44e10aa | ||
|
|
b94d767f86 | ||
|
|
43f17d010a | ||
|
|
fa20e80860 | ||
|
|
485c7640a1 | ||
|
|
d4a7de7c11 | ||
|
|
a6229a2d3e | ||
|
|
abd151f5d7 | ||
|
|
4f1238af71 | ||
|
|
122a1e2af9 | ||
|
|
c8d017bd88 | ||
|
|
23b1b7ba9a | ||
|
|
88943b64e3 | ||
|
|
348401e7b8 | ||
|
|
df248712a5 | ||
|
|
de1b5db70e | ||
|
|
4debe8e567 | ||
|
|
28d362cf28 | ||
|
|
541cdf5960 | ||
|
|
fc6e314498 | ||
|
|
17b5fdd94b | ||
|
|
5b88cc5ded | ||
|
|
db285b3764 | ||
|
|
a425f8c650 | ||
|
|
96f8916e50 | ||
|
|
dd2bf2971e | ||
|
|
34a64cfe53 | ||
|
|
571617ebfc | ||
|
|
3bd8a5729d | ||
|
|
33e4988180 | ||
|
|
663fbfb7a4 | ||
|
|
88aa6a9e30 | ||
|
|
92e2588d6e | ||
|
|
960ead07f2 | ||
|
|
78da1de021 | ||
|
|
946531bd7b | ||
|
|
10009d61a7 | ||
|
|
49fc6b1194 | ||
|
|
6e8b2e161a | ||
|
|
3922f84a2f | ||
|
|
56a50e147d | ||
|
|
7a0b9af662 | ||
|
|
4fbe44605b | ||
|
|
ea7a77236c | ||
|
|
4bbb53a25d | ||
|
|
569295fe30 | ||
|
|
3f5acaa3fb | ||
|
|
17ec22f514 | ||
|
|
a721d36f41 | ||
|
|
eee85cd53c | ||
|
|
7c80778827 | ||
|
|
4c2cc0e36c | ||
|
|
a630e226ba | ||
|
|
0c95a22888 | ||
|
|
30fdf6067e | ||
|
|
cffc5df600 | ||
|
|
3fe8f74e7f | ||
|
|
913937c98d | ||
|
|
b21cd65d30 | ||
|
|
c89638d73c | ||
|
|
98610bfcec | ||
|
|
9c8b3c833f | ||
|
|
4e8c4bfbd2 | ||
|
|
3347679d8f | ||
|
|
05f7d30e5a | ||
|
|
34d8b92dce | ||
|
|
30a4c88843 | ||
|
|
08be94e8e6 | ||
|
|
d544852ff6 | ||
|
|
8dc3233f3b | ||
|
|
c7d479c740 | ||
|
|
a76dc45512 | ||
|
|
9bb5a266dd | ||
|
|
6ebd0bc7a6 | ||
|
|
f8d8d524cf | ||
|
|
1bfc33362e | ||
|
|
5744468c99 | ||
|
|
4d5aead31c | ||
|
|
78a80b8899 | ||
|
|
b961e8101e | ||
|
|
7876125a22 | ||
|
|
e2dcf94598 | ||
|
|
6c1ce27095 | ||
|
|
46e27ae6d5 | ||
|
|
0be5c959da | ||
|
|
face43929d | ||
|
|
dcaa90d21e | ||
|
|
9c259c07aa |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -19,10 +19,15 @@ install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
compile
|
||||
test-driver
|
||||
*.lo
|
||||
*.la
|
||||
stamp-h1
|
||||
*.pyc
|
||||
*.pc
|
||||
/src/jansson_config.h
|
||||
/jansson_private_config.h.in
|
||||
/jansson_private_config.h
|
||||
/build
|
||||
*.exe
|
||||
|
||||
19
.travis.yml
19
.travis.yml
@@ -1,5 +1,22 @@
|
||||
env:
|
||||
matrix:
|
||||
- JANSSON_BUILD_METHOD=cmake JANSSON_CMAKE_OPTIONS="-DJANSSON_TEST_WITH_VALGRIND=ON" JANSSON_EXTRA_INSTALL="valgrind"
|
||||
- JANSSON_BUILD_METHOD=autotools
|
||||
- JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
script: autoreconf -f -i && CFLAGS=-Werror ./configure && make check
|
||||
matrix:
|
||||
exclude:
|
||||
- compiler: clang
|
||||
env: JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
|
||||
allow_failures:
|
||||
- env: JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
|
||||
install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y -qq cmake $JANSSON_EXTRA_INSTALL
|
||||
script:
|
||||
- if [ "$JANSSON_BUILD_METHOD" = "autotools" ]; then autoreconf -f -i && CFLAGS=-Werror ./configure && make check; fi
|
||||
- if [ "$JANSSON_BUILD_METHOD" = "cmake" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && ctest --output-on-failure; fi
|
||||
- if [ "$JANSSON_BUILD_METHOD" = "coverage" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && cmake --build . --target coveralls; fi
|
||||
|
||||
@@ -7,6 +7,7 @@ LOCAL_SRC_FILES := \
|
||||
src/dump.c \
|
||||
src/error.c \
|
||||
src/hashtable.c \
|
||||
src/hashtable_seed.c \
|
||||
src/load.c \
|
||||
src/memory.c \
|
||||
src/pack_unpack.c \
|
||||
@@ -22,7 +23,7 @@ LOCAL_C_INCLUDES += \
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SHARED_LIBRARIES := libc
|
||||
LOCAL_CFLAGS += -O3
|
||||
LOCAL_CFLAGS += -O3 -DHAVE_STDINT_H=1
|
||||
|
||||
LOCAL_MODULE:= libjansson
|
||||
|
||||
|
||||
238
CHANGES
238
CHANGES
@@ -1,3 +1,241 @@
|
||||
Version 2.10
|
||||
============
|
||||
|
||||
Released 2017-03-02
|
||||
|
||||
* New features:
|
||||
|
||||
- Add JSON_EMBED encoding flag allowing arrays and objects to be encoded
|
||||
into existing streams (#329).
|
||||
|
||||
- Add `json_dumpb()` function for dumping to a pre-allocated buffer (#328).
|
||||
|
||||
- Add `json_dumpfd()` and `json_loadfd()` functions for dumping to streaming
|
||||
file descriptors (#328).
|
||||
|
||||
- Add support for parsing buffers larger than 2GB (#309).
|
||||
|
||||
* Build:
|
||||
|
||||
- Fix CMake build when LONG_LONG_INT is defined as "" (#321)
|
||||
|
||||
* Other:
|
||||
|
||||
- Internal code cleanup (#311, #314)
|
||||
|
||||
Version 2.9
|
||||
===========
|
||||
|
||||
Released 2016-09-18
|
||||
|
||||
* New features:
|
||||
|
||||
- Add ``json_auto_t`` to automatically decref a value that goes out
|
||||
of scope. Available only on GCC and Clang. (#301)
|
||||
|
||||
* Build:
|
||||
|
||||
- Fix CMake build (at least on Linux) by removing conflicting
|
||||
jansson_config.h from the distribution (#306)
|
||||
|
||||
- Change CMake install target generation to be optional (#305)
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Small documentation fixes.
|
||||
|
||||
|
||||
Version 2.8
|
||||
===========
|
||||
|
||||
Released 2016-08-30
|
||||
|
||||
* New features:
|
||||
|
||||
- Always preserve insertion order of object items.
|
||||
`json_object_iter()` and friends, `json_object_foreach()` and
|
||||
`json_dumps()` and friends now always work in the insertion order of
|
||||
object items (#293).
|
||||
|
||||
- Add `json_object_foreach_safe()` macro that allows
|
||||
`json_object_del()` calls during iteration (#230).
|
||||
|
||||
- Add `json_get_alloc_funcs()` to allow reading the allocation
|
||||
functions set by `json_set_alloc_funcs()` (#262, #264).
|
||||
|
||||
- Add `json_pack()` format specifiers s?, o? and O? for values that
|
||||
can be null (#261, #270).
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix a crash when parsing inputs consisting of very deeply nested
|
||||
arrays or objects (#282, #284).
|
||||
|
||||
- Never convert numbers to integers in the parser when
|
||||
JSON_DECODE_INT_AS_REAL is set. This fixes error messages for
|
||||
overflowing numbers when JSON_DECODE_INT_AS_REAL is set (#212).
|
||||
|
||||
- Fix a use-after-free in `json_pack()` error handling.
|
||||
|
||||
- Fix subnormal number parsing on mingw32.
|
||||
|
||||
- Handle out-of-memory situations gracefully in the hashtable
|
||||
implementation (#298).
|
||||
|
||||
* Build:
|
||||
|
||||
- Fix build with CMake on all versions of Visual Studio up to 2015
|
||||
(#262, #289).
|
||||
|
||||
- Fix pkgconfig libdir when using CMake (#268).
|
||||
|
||||
- Fix CMake config for static CRT builds on Windows (#206).
|
||||
|
||||
- Fix warnings on LLVM 6.0 targeting iOS arm64 (#208).
|
||||
|
||||
- Add coverlls.io support via Travis for a nice test coverage badge
|
||||
(#211).
|
||||
|
||||
- Don't expect ``jansson_config.h`` to be in the compiler's include
|
||||
path (#209).
|
||||
|
||||
- Add a build-time option to set initial hashtable size (#213).
|
||||
|
||||
- Use snprintf and strncpy in place of sprintf and strcpy to silence
|
||||
linker warnings on OpenBSD (#233).
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Fix various typos in documentation, and a broken link (#258).
|
||||
|
||||
- Add an example program in ``examples/`` (#214, #217).
|
||||
|
||||
- Fix building of documentation man pages (#207).
|
||||
|
||||
- Document the fact that copying objects doesn't preserve the
|
||||
insertion order of keys (#237).
|
||||
|
||||
* Tests:
|
||||
|
||||
- Don't use the nonstandard __FUNCTION__ macro in tests.
|
||||
|
||||
- Use expr instead of $((...)) in shell scripts for Solaris 10
|
||||
compatibility.
|
||||
|
||||
- Disable Visual Studio warning C4756 when triggered deliberately in
|
||||
tests (#216).
|
||||
|
||||
- Other minor fixes (#221, #248).
|
||||
|
||||
* Other changes:
|
||||
|
||||
- List all unrecognized object keys when strict unpacking fails
|
||||
(#263).
|
||||
|
||||
- Alter the order of the members of the hashtable_pair struct for
|
||||
easier debugging.
|
||||
|
||||
- Minor performance improvement to `json_dump()` and friends (#234).
|
||||
|
||||
- Minor style fixes (#255, #257).
|
||||
|
||||
|
||||
Version 2.7
|
||||
===========
|
||||
|
||||
Released 2014-10-02
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_pack()` and friends: Add format specifiers ``s%`` and ``+%``
|
||||
for a size_t string length (#141).
|
||||
|
||||
- `json_unpack()` and friends: Add format specifier ``s%`` for
|
||||
unpacking the string length along with the string itself (#141).
|
||||
|
||||
- Add length-aware string constructors `json_stringn()` and
|
||||
`json_stringn_nocheck()`, length-aware string mutators
|
||||
`json_string_setn()` and `json_string_setn_nocheck()`, and a
|
||||
function for getting string's length `json_string_length()` (#141,
|
||||
#143).
|
||||
|
||||
- Support ``\u0000`` escapes in the decoder. The support can be
|
||||
enabled by using the ``JSON_ALLOW_NUL`` decoding flag (#141).
|
||||
|
||||
- Add `json_boolean_value()` as an alias for `json_is_true()`
|
||||
(#146).
|
||||
|
||||
- Add JSON_REAL_PRECISION encoding flag/macro for controlling real
|
||||
number precision (#178).
|
||||
|
||||
- Define the maximum indentation as JSON_MAX_INDENT (#191).
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Some malformed ``\uNNNN`` escapes could crash the decoder with an
|
||||
assertion failure.
|
||||
|
||||
- Avoid integer overflows with very long strings in UTF-8 decoder and
|
||||
hashtable.
|
||||
|
||||
- Check for *NULL* key in `json_object_get()` and
|
||||
`json_object_del()` (#151).
|
||||
|
||||
- Enhance hashtable seeding on Windows (#162).
|
||||
|
||||
- `json_unpack()`: Allow mixing JSON_STRICT with optional keys
|
||||
(#162, #163).
|
||||
|
||||
- Fix int/int32 mismatch (#142).
|
||||
|
||||
- Parse subnormal numbers correctly (#202).
|
||||
|
||||
* Build:
|
||||
|
||||
- Remove VS2010 build files. CMake should be used on Windows instead
|
||||
(#165).
|
||||
|
||||
- Fix CMake build flags for MinGW (#193).
|
||||
|
||||
- Add CMake config files for find_package. Rename config.h to
|
||||
jansson_private_config.h (#157, #159).
|
||||
|
||||
- Make Valgrind checks work with CMake (#160).
|
||||
|
||||
- Fix feature checks to use correct __ATOMIC flags.
|
||||
|
||||
- Fix CMake checks for uint16_t and uint8_t support (#177).
|
||||
|
||||
- Make Jansson build on SmartOS/Solaris (#171).
|
||||
|
||||
- Work around a GCC bug on Solaris (#175).
|
||||
|
||||
- Fix autoreconf on Debian (#182).
|
||||
|
||||
- Don't use GNU make specific export for global AM_CFLAGS (#203,
|
||||
#204).
|
||||
|
||||
- Fix building on Android using the supplied Android.mk (#166,
|
||||
#174).
|
||||
|
||||
- Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS (#200).
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Document JANSSON_BUILD_SHARED_LIBS CMake option (#187).
|
||||
|
||||
* Tests:
|
||||
|
||||
- Close file handles correctly (#198).
|
||||
|
||||
* Other changes:
|
||||
|
||||
- ``\uNNNN`` escapes are now encoded in upper case for better
|
||||
readability.
|
||||
|
||||
- Enable usage of AddressSanitizer (#180).
|
||||
|
||||
|
||||
Version 2.6
|
||||
===========
|
||||
|
||||
|
||||
372
CMakeLists.txt
372
CMakeLists.txt
@@ -51,43 +51,51 @@ cmake_minimum_required (VERSION 2.8)
|
||||
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)
|
||||
option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF)
|
||||
option(USE_URANDOM "Use /dev/urandom to seed the hash function." ON)
|
||||
option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." ON)
|
||||
|
||||
if (MSVC)
|
||||
# This option must match the settings used in your program, in particular if you
|
||||
# are linking statically
|
||||
OPTION( STATIC_CRT "Link the static CRT libraries" OFF )
|
||||
option(JANSSON_STATIC_CRT "Link the static CRT libraries" OFF )
|
||||
endif ()
|
||||
|
||||
option(JANSSON_EXAMPLES "Compile example applications" ON)
|
||||
|
||||
if (UNIX)
|
||||
option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF)
|
||||
option(JANSSON_COVERALLS "Generate coverage info for Coveralls" OFF)
|
||||
option(JANSSON_COVERALLS_UPLOAD "Upload coverage info to Coveralls (Only works via Travis)" ON)
|
||||
endif ()
|
||||
|
||||
# Set some nicer output dirs.
|
||||
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
set(JANSSON_TEMP_DIR ${PROJECT_BINARY_DIR}/tmp)
|
||||
|
||||
# Give the debug version a different postfix for windows,
|
||||
# so both the debug and release version can be built in the
|
||||
# same build-tree on Windows (MSVC).
|
||||
if (WIN32)
|
||||
SET (CMAKE_DEBUG_POSTFIX "_d")
|
||||
else (WIN32)
|
||||
set(CMAKE_DEBUG_POSTFIX "_d")
|
||||
endif (WIN32)
|
||||
|
||||
# This is how I thought it should go
|
||||
# set (JANSSON_VERSION "2.3.1")
|
||||
# set (JANSSON_SOVERSION 2)
|
||||
|
||||
set(JANSSON_DISPLAY_VERSION "2.6")
|
||||
set(JANSSON_DISPLAY_VERSION "2.10")
|
||||
|
||||
# This is what is required to match the same numbers as automake's
|
||||
set (JANSSON_VERSION "4.6.0")
|
||||
set (JANSSON_SOVERSION 4)
|
||||
set(JANSSON_VERSION "4.10.0")
|
||||
set(JANSSON_SOVERSION 4)
|
||||
|
||||
# for CheckFunctionKeywords
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
INCLUDE (CheckCSourceCompiles)
|
||||
include (CheckCSourceCompiles)
|
||||
include (CheckFunctionExists)
|
||||
include (CheckFunctionKeywords)
|
||||
include (CheckIncludeFiles)
|
||||
@@ -97,15 +105,30 @@ if (MSVC)
|
||||
# Turn off Microsofts "security" warnings.
|
||||
add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" )
|
||||
|
||||
if (STATIC_CRT)
|
||||
set(CMAKE_C_FLAGS_RELEASE "/MT")
|
||||
set(CMAKE_C_FLAGS_DEBUG "/MTd")
|
||||
if (JANSSON_STATIC_CRT)
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "-fPIC")
|
||||
if (NOT WIN32 AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX))
|
||||
add_definitions("-fPIC")
|
||||
endif()
|
||||
|
||||
message("C compiler: ${CMAKE_C_COMPILER_ID}")
|
||||
|
||||
# Coverage only works with GCC for a debug build.
|
||||
if (JANSSON_COVERALLS)
|
||||
set(JANSSON_COVERAGE ON)
|
||||
endif()
|
||||
|
||||
if (JANSSON_COVERAGE)
|
||||
include(CodeCoverage)
|
||||
include(Coveralls)
|
||||
|
||||
# This adds coverage arguments to gcc/clang.
|
||||
coveralls_turn_on_coverage()
|
||||
endif()
|
||||
|
||||
check_include_files (endian.h HAVE_ENDIAN_H)
|
||||
@@ -149,10 +172,12 @@ 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)
|
||||
check_type_size ("unsigned short" UNSIGNED_SHORT)
|
||||
|
||||
check_type_size (uint32_t UINT32_T)
|
||||
check_type_size (__uint32 __UINT32)
|
||||
if (HAVE_UINT32_T)
|
||||
set (JSON_UINT32 uint32_t)
|
||||
elseif (HAVE___UINT32)
|
||||
@@ -165,6 +190,30 @@ else ()
|
||||
message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type")
|
||||
endif ()
|
||||
|
||||
check_type_size (uint16_t UINT16_T)
|
||||
check_type_size (__uint16 __UINT16)
|
||||
if (HAVE_UINT16_T)
|
||||
set (JSON_UINT16 uint16_t)
|
||||
elseif (HAVE___UINT16)
|
||||
set (JSON_UINT16 __uint16)
|
||||
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 2))
|
||||
set (JSON_UINT16 "unsigned int")
|
||||
elseif (HAVE_UNSIGNED_SHORT AND (${UNSIGNED_SHORT} EQUAL 2))
|
||||
set (JSON_UINT16 "unsigned short")
|
||||
else ()
|
||||
message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type")
|
||||
endif ()
|
||||
|
||||
check_type_size (uint8_t UINT8_T)
|
||||
check_type_size (__uint8 __UINT8)
|
||||
if (HAVE_UINT8_T)
|
||||
set (JSON_UINT8 uint8_t)
|
||||
elseif (HAVE___UINT8)
|
||||
set (JSON_UINT8 __uint8)
|
||||
else ()
|
||||
set (JSON_UINT8 "unsigned char")
|
||||
endif ()
|
||||
|
||||
# Check for ssize_t and SSIZE_T existance.
|
||||
check_type_size(ssize_t SSIZE_T)
|
||||
check_type_size(SSIZE_T UPPERCASE_SSIZE_T)
|
||||
@@ -201,7 +250,7 @@ endif ()
|
||||
# detect what to use for the 64 bit type.
|
||||
# Note: I will prefer long long if I can get it, as that is what the automake system aimed for.
|
||||
if (NOT DEFINED JSON_INT_T)
|
||||
if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8))
|
||||
if (HAVE_LONG_LONG_INT AND ((${LONG_LONG_INT}) EQUAL 8))
|
||||
set (JSON_INT_T "long long")
|
||||
elseif (HAVE_INT64_T)
|
||||
set (JSON_INT_T int64_t)
|
||||
@@ -232,10 +281,10 @@ if (HAVE_LOCALECONV AND HAVE_LOCALE_H)
|
||||
set (JSON_HAVE_LOCALECONV 1)
|
||||
else ()
|
||||
set (JSON_HAVE_LOCALECONV 0)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# check if we have setlocale
|
||||
check_function_exists (setlocale 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.
|
||||
@@ -244,41 +293,20 @@ check_function_keywords("__inline")
|
||||
check_function_keywords("__inline__")
|
||||
|
||||
if (HAVE_INLINE)
|
||||
set (JSON_INLINE inline)
|
||||
set(JSON_INLINE inline)
|
||||
elseif (HAVE___INLINE)
|
||||
set (JSON_INLINE __inline)
|
||||
set(JSON_INLINE __inline)
|
||||
elseif (HAVE___INLINE__)
|
||||
set (JSON_INLINE __inline__)
|
||||
else (HAVE_INLINE)
|
||||
set(JSON_INLINE __inline__)
|
||||
else()
|
||||
# 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 ()
|
||||
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)
|
||||
set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.")
|
||||
|
||||
# configure the public config file
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
|
||||
@@ -288,59 +316,66 @@ configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
|
||||
file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/)
|
||||
|
||||
add_definitions(-DJANSSON_USING_CMAKE)
|
||||
|
||||
# configure the private config file
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/private_include/config.h)
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_private_config.h.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
|
||||
|
||||
# and tell the source code to include it
|
||||
add_definitions (-DHAVE_CONFIG_H)
|
||||
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)
|
||||
file(GLOB JANSSON_SRC src/*.c)
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(JANSSON_HDR_PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hashtable.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson_private.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/strbuffer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utf.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
|
||||
|
||||
add_library (jansson SHARED ${C_FILES} src/jansson.def)
|
||||
set(JANSSON_HDR_PUBLIC
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h)
|
||||
|
||||
set_target_properties (jansson PROPERTIES
|
||||
source_group("Library Sources" FILES ${JANSSON_SRC})
|
||||
source_group("Library Private Headers" FILES ${JANSSON_HDR_PRIVATE})
|
||||
source_group("Library Public Headers" FILES ${JANSSON_HDR_PUBLIC})
|
||||
|
||||
if(JANSSON_BUILD_SHARED_LIBS)
|
||||
add_library(jansson SHARED
|
||||
${JANSSON_SRC}
|
||||
${JANSSON_HDR_PRIVATE}
|
||||
${JANSSON_HDR_PUBLIC}
|
||||
src/jansson.def)
|
||||
|
||||
set_target_properties(jansson PROPERTIES
|
||||
VERSION ${JANSSON_VERSION}
|
||||
SOVERSION ${JANSSON_SOVERSION})
|
||||
else()
|
||||
add_library(jansson
|
||||
${JANSSON_SRC}
|
||||
${JANSSON_HDR_PRIVATE}
|
||||
${JANSSON_HDR_PUBLIC})
|
||||
endif()
|
||||
|
||||
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)
|
||||
if (JANSSON_EXAMPLES)
|
||||
add_executable(simple_parse "${PROJECT_SOURCE_DIR}/examples/simple_parse.c")
|
||||
target_link_libraries(simple_parse jansson)
|
||||
endif()
|
||||
|
||||
# For building Documentation (uses Sphinx)
|
||||
OPTION (BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
|
||||
if (BUILD_DOCS)
|
||||
option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
|
||||
if (JANSSON_BUILD_DOCS)
|
||||
find_package(Sphinx)
|
||||
|
||||
if (NOT SPHINX_FOUND)
|
||||
message(WARNING "Sphinx not found. Cannot generate documentation!
|
||||
Set -DBUILD_DOCS=0 to get rid of this message.")
|
||||
Set -DJANSSON_BUILD_DOCS=OFF to get rid of this message.")
|
||||
else()
|
||||
if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
|
||||
message(WARNING "Your Sphinx version is too old!
|
||||
@@ -375,9 +410,9 @@ if (BUILD_DOCS)
|
||||
# Add documentation targets.
|
||||
set(DOC_TARGETS html)
|
||||
|
||||
OPTION(BUILD_MAN "Create a target for building man pages." ON)
|
||||
option(JANSSON_BUILD_MAN "Create a target for building man pages." ON)
|
||||
|
||||
if (BUILD_MAN)
|
||||
if (JANSSON_BUILD_MAN)
|
||||
if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
|
||||
message(WARNING "Sphinx version 1.0 > is required to build man pages. You have v${Sphinx_VERSION_STRING}.")
|
||||
else()
|
||||
@@ -385,9 +420,9 @@ if (BUILD_DOCS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
OPTION(BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF)
|
||||
option(JANSSON_BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF)
|
||||
|
||||
if (BUILD_LATEX)
|
||||
if (JANSSON_BUILD_LATEX)
|
||||
find_package(LATEX)
|
||||
|
||||
if (NOT LATEX_COMPILER)
|
||||
@@ -420,14 +455,14 @@ if (BUILD_DOCS)
|
||||
endif ()
|
||||
|
||||
|
||||
OPTION (WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF)
|
||||
option(JANSSON_WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF)
|
||||
|
||||
if (NOT WITHOUT_TESTS)
|
||||
OPTION (TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
|
||||
if (NOT JANSSON_WITHOUT_TESTS)
|
||||
option(JANSSON_TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
|
||||
|
||||
ENABLE_TESTING()
|
||||
|
||||
if (TEST_WITH_VALGRIND)
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
# TODO: Add FindValgrind.cmake instead of having a hardcoded path.
|
||||
|
||||
add_definitions(-DVALGRIND)
|
||||
@@ -435,7 +470,7 @@ if (NOT WITHOUT_TESTS)
|
||||
# enable valgrind
|
||||
set(CMAKE_MEMORYCHECK_COMMAND valgrind)
|
||||
set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS
|
||||
"--leak-check=full --show-reachable=yes --track-origins=yes -q")
|
||||
"--error-exitcode=1 --leak-check=full --show-reachable=yes --track-origins=yes -q")
|
||||
|
||||
set(MEMCHECK_COMMAND
|
||||
"${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}")
|
||||
@@ -446,7 +481,7 @@ if (NOT WITHOUT_TESTS)
|
||||
# Test suites.
|
||||
#
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
add_definitions(-Wall -Wextra -Wdeclaration-after-statement -Werror)
|
||||
add_definitions(-Wall -Wextra -Wdeclaration-after-statement)
|
||||
endif ()
|
||||
|
||||
set(api_tests
|
||||
@@ -479,34 +514,171 @@ if (NOT WITHOUT_TESTS)
|
||||
# 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})
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
add_test(memcheck__${test}
|
||||
${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
|
||||
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
|
||||
else()
|
||||
add_test(${test}
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
|
||||
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Test harness for the suites tests.
|
||||
build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin)
|
||||
|
||||
set(SUITE_TEST_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process)
|
||||
set(SUITES encoding-flags valid invalid invalid-unicode)
|
||||
foreach (SUITE ${SUITES})
|
||||
file(GLOB TESTDIRS ${jansson_SOURCE_DIR}/test/suites/${SUITE}/*)
|
||||
|
||||
foreach (TESTDIR ${TESTDIRS})
|
||||
if (IS_DIRECTORY ${TESTDIR})
|
||||
get_filename_component(TNAME ${TESTDIR} NAME)
|
||||
add_test(${SUITE}__${TNAME}
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process ${TESTDIR})
|
||||
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
add_test(memcheck__${SUITE}__${TNAME}
|
||||
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR})
|
||||
else()
|
||||
add_test(${SUITE}__${TNAME}
|
||||
${SUITE_TEST_CMD} ${TESTDIR})
|
||||
endif()
|
||||
|
||||
if ((${SUITE} STREQUAL "valid" OR ${SUITE} STREQUAL "invalid") AND NOT EXISTS ${TESTDIR}/nostrip)
|
||||
add_test(${SUITE}__${TNAME}__strip
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process --strip ${TESTDIR})
|
||||
if (JANSSON_TEST_WITH_VALGRIND)
|
||||
add_test(memcheck__${SUITE}__${TNAME}__strip
|
||||
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} --strip ${TESTDIR})
|
||||
else()
|
||||
add_test(${SUITE}__${TNAME}__strip
|
||||
${SUITE_TEST_CMD} --strip ${TESTDIR})
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
endforeach ()
|
||||
|
||||
if (JANSSON_COVERAGE)
|
||||
setup_target_for_coverage(
|
||||
coverage # Coverage make target "make coverage".
|
||||
coverage # Name of output directory.
|
||||
make # Name of test runner executable.
|
||||
test) # Arguments to the test runner above (make test).
|
||||
|
||||
if (JANSSON_COVERALLS)
|
||||
set(COVERAGE_SRCS ${JANSSON_SRC})
|
||||
coveralls_setup("${COVERAGE_SRCS}" ${JANSSON_COVERALLS_UPLOAD})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Enable using "make check" just like the autotools project.
|
||||
# By default cmake creates a target "make test"
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
|
||||
DEPENDS json_process ${api_tests})
|
||||
endif ()
|
||||
|
||||
#
|
||||
# Installation preparation.
|
||||
#
|
||||
|
||||
# Allow the user to override installation directories.
|
||||
set(JANSSON_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
|
||||
set(JANSSON_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables")
|
||||
set(JANSSON_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files")
|
||||
|
||||
if(WIN32 AND NOT CYGWIN)
|
||||
set(DEF_INSTALL_CMAKE_DIR cmake)
|
||||
else()
|
||||
set(DEF_INSTALL_CMAKE_DIR lib/cmake/jansson)
|
||||
endif()
|
||||
|
||||
set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
|
||||
|
||||
# Create pkg-conf file.
|
||||
# (We use the same files as ./configure does, so we
|
||||
# have to defined the same variables used there).
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(libdir ${CMAKE_INSTALL_PREFIX}/${JANSSON_INSTALL_LIB_DIR})
|
||||
set(VERSION ${JANSSON_DISPLAY_VERSION})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
|
||||
|
||||
# Make sure the paths are absolute.
|
||||
foreach(p LIB BIN INCLUDE CMAKE)
|
||||
set(var JANSSON_INSTALL_${p}_DIR)
|
||||
if(NOT IS_ABSOLUTE "${${var}}")
|
||||
set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Export targets (This is used for other CMake projects to easily find the libraries and include files).
|
||||
export(TARGETS jansson
|
||||
FILE "${PROJECT_BINARY_DIR}/JanssonTargets.cmake")
|
||||
export(PACKAGE jansson)
|
||||
|
||||
# Generate the config file for the build-tree.
|
||||
set(JANSSON__INCLUDE_DIRS
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${PROJECT_BINARY_DIR}/include")
|
||||
set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories")
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
|
||||
${PROJECT_BINARY_DIR}/JanssonConfig.cmake
|
||||
@ONLY)
|
||||
|
||||
# Generate the config file for the installation tree.
|
||||
file(RELATIVE_PATH
|
||||
REL_INCLUDE_DIR
|
||||
"${JANSSON_INSTALL_CMAKE_DIR}"
|
||||
"${JANSSON_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir.
|
||||
|
||||
# Note the EVENT_CMAKE_DIR is defined in JanssonConfig.cmake.in,
|
||||
# we escape it here so it's evaluated when it is included instead
|
||||
# so that the include dirs are given relative to where the
|
||||
# config file is located.
|
||||
set(JANSSON__INCLUDE_DIRS
|
||||
"\${JANSSON_CMAKE_DIR}/${REL_INCLUDE_DIR}")
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
|
||||
${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
|
||||
@ONLY)
|
||||
|
||||
# Generate version info for both build-tree and install-tree.
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfigVersion.cmake.in
|
||||
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
|
||||
@ONLY)
|
||||
|
||||
# Define the public headers.
|
||||
set_target_properties(jansson PROPERTIES PUBLIC_HEADER "${JANSSON_HDR_PUBLIC}")
|
||||
#TODO: fix this.
|
||||
|
||||
#
|
||||
# Install targets.
|
||||
#
|
||||
option(JANSSON_INSTALL "Generate installation target" ON)
|
||||
if (JANSSON_INSTALL)
|
||||
install(TARGETS jansson
|
||||
EXPORT JanssonTargets
|
||||
LIBRARY DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
|
||||
ARCHIVE DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
|
||||
RUNTIME DESTINATION "${JANSSON_INSTALL_BIN_DIR}" COMPONENT lib # Windows DLLs
|
||||
PUBLIC_HEADER DESTINATION "${JANSSON_INSTALL_INCLUDE_DIR}" COMPONENT dev)
|
||||
|
||||
# Install the pkg-config.
|
||||
install (FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
|
||||
DESTINATION ${JANSSON_INSTALL_LIB_DIR}/pkgconfig COMPONENT dev)
|
||||
|
||||
# Install the configs.
|
||||
install(FILES
|
||||
${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
|
||||
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
|
||||
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
|
||||
|
||||
# Install exports for the install-tree.
|
||||
install(EXPORT JanssonTargets
|
||||
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
|
||||
endif()
|
||||
|
||||
# For use when simply using add_library from a parent project to build jansson.
|
||||
set(JANSSON_LIBRARIES jansson CACHE STRING "Jansson libraries")
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst win32 CMakeLists.txt cmake
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android examples
|
||||
SUBDIRS = doc src test
|
||||
|
||||
# "make distcheck" builds the dvi target, so use it to check that the
|
||||
@@ -8,8 +8,3 @@ dvi:
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = jansson.pc
|
||||
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
export AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement
|
||||
endif
|
||||
|
||||
13
README.rst
13
README.rst
@@ -2,15 +2,20 @@ Jansson README
|
||||
==============
|
||||
|
||||
.. image:: https://travis-ci.org/akheron/jansson.png
|
||||
:alt: Build status
|
||||
:target: https://travis-ci.org/akheron/jansson
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/lmhkkc4q8cwc65ko
|
||||
:target: https://ci.appveyor.com/project/akheron/jansson
|
||||
|
||||
.. image:: https://coveralls.io/repos/akheron/jansson/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/akheron/jansson?branch=master
|
||||
|
||||
Jansson_ is a C library for encoding, decoding and manipulating JSON
|
||||
data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Comprehensive documentation
|
||||
- `Comprehensive documentation`_
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
@@ -46,8 +51,7 @@ use autoreconf::
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Prebuilt HTML documentation is available at
|
||||
http://www.digip.org/jansson/doc/.
|
||||
Documentation is available at http://jansson.readthedocs.io/en/latest/.
|
||||
|
||||
The documentation source is in the ``doc/`` subdirectory. To generate
|
||||
HTML documentation, invoke::
|
||||
@@ -59,5 +63,6 @@ Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
|
||||
|
||||
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
.. _`Comprehensive documentation`: http://jansson.readthedocs.io/en/latest/
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -36,4 +36,8 @@
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV 0
|
||||
|
||||
/* Maximum recursion depth for parsing JSON input.
|
||||
This limits the depth of e.g. array-within-array constructions. */
|
||||
#define JSON_PARSER_MAX_DEPTH 2048
|
||||
|
||||
#endif
|
||||
|
||||
14
appveyor.yml
Normal file
14
appveyor.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
environment:
|
||||
matrix:
|
||||
- VS: Visual Studio 9 2008
|
||||
- VS: Visual Studio 10 2010
|
||||
- VS: Visual Studio 11 2012
|
||||
- VS: Visual Studio 12 2013
|
||||
- VS: Visual Studio 14 2015
|
||||
|
||||
build_script:
|
||||
- md build
|
||||
- cd build
|
||||
- cmake -G "%VS%" ..
|
||||
- cmake --build . --config Release
|
||||
- ctest --output-on-failure
|
||||
163
cmake/CodeCoverage.cmake
Normal file
163
cmake/CodeCoverage.cmake
Normal file
@@ -0,0 +1,163 @@
|
||||
#
|
||||
# Boost Software License - Version 1.0 - August 17th, 2003
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person or organization
|
||||
# obtaining a copy of the software and accompanying documentation covered by
|
||||
# this license (the "Software") to use, reproduce, display, distribute,
|
||||
# execute, and transmit the Software, and to prepare derivative works of the
|
||||
# Software, and to permit third-parties to whom the Software is furnished to
|
||||
# do so, all subject to the following:
|
||||
#
|
||||
# The copyright notices in the Software and this entire statement, including
|
||||
# the above license grant, this restriction and the following disclaimer,
|
||||
# must be included in all copies of the Software, in whole or in part, and
|
||||
# all derivative works of the Software, unless such copies or derivative
|
||||
# works are solely in the form of machine-executable object code generated by
|
||||
# a source language processor.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# USAGE:
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt:
|
||||
# INCLUDE(CodeCoverage)
|
||||
#
|
||||
# 3. Set compiler flags to turn off optimization and enable coverage:
|
||||
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
#
|
||||
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
|
||||
# which runs your test executable and produces a lcov code coverage report:
|
||||
# Example:
|
||||
# SETUP_TARGET_FOR_COVERAGE(
|
||||
# my_coverage_target # Name for custom target.
|
||||
# test_driver # Name of the test driver executable that runs the tests.
|
||||
# # NOTE! This should always have a ZERO as exit code
|
||||
# # otherwise the coverage generation will not complete.
|
||||
# coverage # Name of output directory.
|
||||
# )
|
||||
#
|
||||
# 4. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
#
|
||||
|
||||
# Check prereqs
|
||||
FIND_PROGRAM( GCOV_PATH gcov )
|
||||
FIND_PROGRAM( LCOV_PATH lcov )
|
||||
FIND_PROGRAM( GENHTML_PATH genhtml )
|
||||
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
|
||||
|
||||
IF(NOT GCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
|
||||
ENDIF() # NOT GCOV_PATH
|
||||
|
||||
IF(NOT (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC))
|
||||
# Clang version 3.0.0 and greater now supports gcov as well.
|
||||
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
|
||||
|
||||
IF(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
|
||||
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...")
|
||||
ENDIF()
|
||||
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
|
||||
|
||||
IF ( NOT CMAKE_BUILD_TYPE STREQUAL "Debug" )
|
||||
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
|
||||
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _outputname lcov output is generated as _outputname.info
|
||||
# HTML report is generated in _outputname/index.html
|
||||
# Param _testrunner The name of the target which runs the tests.
|
||||
# MUST return ZERO always, even on errors.
|
||||
# If not, no coverage report will be created!
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner)
|
||||
|
||||
IF(NOT LCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
|
||||
ENDIF() # NOT LCOV_PATH
|
||||
|
||||
IF(NOT GENHTML_PATH)
|
||||
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
ENDIF() # NOT GENHTML_PATH
|
||||
|
||||
# Setup target
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Cleanup lcov
|
||||
${LCOV_PATH} --directory . --zerocounters
|
||||
|
||||
# Run tests
|
||||
COMMAND ${_testrunner} ${ARGV3}
|
||||
|
||||
# Capturing lcov counters and generating report
|
||||
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
|
||||
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
|
||||
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests
|
||||
# Param _outputname cobertura output is generated as _outputname.xml
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT PYTHON_EXECUTABLE)
|
||||
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
|
||||
ENDIF() # NOT PYTHON_EXECUTABLE
|
||||
|
||||
IF(NOT GCOVR_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
ENDIF() # NOT GCOVR_PATH
|
||||
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Run tests
|
||||
${_testrunner} ${ARGV3}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
|
||||
|
||||
111
cmake/Coveralls.cmake
Normal file
111
cmake/Coveralls.cmake
Normal file
@@ -0,0 +1,111 @@
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Param _COVERAGE_SRCS A list of source files that coverage should be collected for.
|
||||
# Param _COVERALLS_UPLOAD Upload the result to coveralls?
|
||||
#
|
||||
function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD)
|
||||
# When passing a CMake list to an external process, the list
|
||||
# will be converted from the format "1;2;3" to "1 2 3".
|
||||
# This means the script we're calling won't see it as a list
|
||||
# of sources, but rather just one long path. We remedy this
|
||||
# by replacing ";" with "*" and then reversing that in the script
|
||||
# that we're calling.
|
||||
# http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html
|
||||
set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS})
|
||||
set(COVERAGE_SRCS "")
|
||||
foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP})
|
||||
set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}")
|
||||
endforeach()
|
||||
|
||||
#message("Coverage sources: ${COVERAGE_SRCS}")
|
||||
set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json)
|
||||
|
||||
add_custom_target(coveralls_generate
|
||||
|
||||
# Zero the coverage counters.
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/CoverallsClear.cmake"
|
||||
|
||||
# Run regress tests.
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
||||
|
||||
# Generate Gcov and translate it into coveralls JSON.
|
||||
# We do this by executing an external CMake script.
|
||||
# (We don't want this to run at CMake generation time, but after compilation and everything has run).
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c"
|
||||
-DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}"
|
||||
-DCOV_PATH="${PROJECT_BINARY_DIR}"
|
||||
-DPROJECT_ROOT="${PROJECT_SOURCE_DIR}"
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/CoverallsGenerateGcov.cmake"
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
COMMENT "Generating coveralls output..."
|
||||
)
|
||||
|
||||
if (_COVERALLS_UPLOAD)
|
||||
message("COVERALLS UPLOAD: ON")
|
||||
|
||||
find_program(CURL_EXECUTABLE curl)
|
||||
|
||||
if (NOT CURL_EXECUTABLE)
|
||||
message(FATAL_ERROR "Coveralls: curl not found! Aborting")
|
||||
endif()
|
||||
|
||||
add_custom_target(coveralls_upload
|
||||
# Upload the JSON to coveralls.
|
||||
COMMAND ${CURL_EXECUTABLE}
|
||||
-S -F json_file=@${COVERALLS_FILE}
|
||||
https://coveralls.io/api/v1/jobs
|
||||
|
||||
DEPENDS coveralls_generate
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
COMMENT "Uploading coveralls output...")
|
||||
|
||||
add_custom_target(coveralls DEPENDS coveralls_upload)
|
||||
else()
|
||||
message("COVERALLS UPLOAD: OFF")
|
||||
add_custom_target(coveralls DEPENDS coveralls_generate)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
macro(coveralls_turn_on_coverage)
|
||||
if(NOT (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
AND (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
|
||||
message(FATAL_ERROR "Coveralls: Compiler ${CMAKE_C_COMPILER_ID} is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake <options> ..")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
||||
endmacro()
|
||||
|
||||
|
||||
|
||||
24
cmake/CoverallsClear.cmake
Normal file
24
cmake/CoverallsClear.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
|
||||
#
|
||||
|
||||
file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda)
|
||||
|
||||
380
cmake/CoverallsGenerateGcov.cmake
Normal file
380
cmake/CoverallsGenerateGcov.cmake
Normal file
@@ -0,0 +1,380 @@
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
|
||||
#
|
||||
# This is intended to be run by a custom target in a CMake project like this.
|
||||
# 0. Compile program with coverage support.
|
||||
# 1. Clear coverage data. (Recursively delete *.gcda in build dir)
|
||||
# 2. Run the unit tests.
|
||||
# 3. Run this script specifying which source files the coverage should be performed on.
|
||||
#
|
||||
# This script will then use gcov to generate .gcov files in the directory specified
|
||||
# via the COV_PATH var. This should probably be the same as your cmake build dir.
|
||||
#
|
||||
# It then parses the .gcov files to convert them into the Coveralls JSON format:
|
||||
# https://coveralls.io/docs/api
|
||||
#
|
||||
# Example for running as standalone CMake script from the command line:
|
||||
# (Note it is important the -P is at the end...)
|
||||
# $ cmake -DCOV_PATH=$(pwd)
|
||||
# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
|
||||
# -P ../cmake/CoverallsGcovUpload.cmake
|
||||
#
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
|
||||
#
|
||||
# Make sure we have the needed arguments.
|
||||
#
|
||||
if (NOT COVERALLS_OUTPUT_FILE)
|
||||
message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
|
||||
endif()
|
||||
|
||||
if (NOT COV_PATH)
|
||||
message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
|
||||
endif()
|
||||
|
||||
if (NOT COVERAGE_SRCS)
|
||||
message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
|
||||
endif()
|
||||
|
||||
if (NOT PROJECT_ROOT)
|
||||
message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
|
||||
endif()
|
||||
|
||||
# Since it's not possible to pass a CMake list properly in the
|
||||
# "1;2;3" format to an external process, we have replaced the
|
||||
# ";" with "*", so reverse that here so we get it back into the
|
||||
# CMake list format.
|
||||
string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
|
||||
|
||||
find_program(GCOV_EXECUTABLE gcov)
|
||||
|
||||
if (NOT GCOV_EXECUTABLE)
|
||||
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||
endif()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
# TODO: Add these git things to the coveralls json.
|
||||
if (GIT_FOUND)
|
||||
# Branch.
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
macro (git_log_format FORMAT_CHARS VAR_NAME)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ${VAR_NAME}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endmacro()
|
||||
|
||||
git_log_format(an GIT_AUTHOR_EMAIL)
|
||||
git_log_format(ae GIT_AUTHOR_EMAIL)
|
||||
git_log_format(cn GIT_COMMITTER_NAME)
|
||||
git_log_format(ce GIT_COMMITTER_EMAIL)
|
||||
git_log_format(B GIT_COMMIT_MESSAGE)
|
||||
|
||||
message("Git exe: ${GIT_EXECUTABLE}")
|
||||
message("Git branch: ${GIT_BRANCH}")
|
||||
message("Git author: ${GIT_AUTHOR_NAME}")
|
||||
message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
|
||||
message("Git commiter name: ${GIT_COMMITTER_NAME}")
|
||||
message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
|
||||
message("Git commit message: ${GIT_COMMIT_MESSAGE}")
|
||||
|
||||
endif()
|
||||
|
||||
############################# Macros #########################################
|
||||
|
||||
#
|
||||
# This macro converts from the full path format gcov outputs:
|
||||
#
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
#
|
||||
# to the original source file path the .gcov is for:
|
||||
#
|
||||
# /path/to/project/root/subdir/the_file.c
|
||||
#
|
||||
macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
|
||||
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
# ->
|
||||
# #path#to#project#root#subdir#the_file.c.gcov
|
||||
get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
|
||||
|
||||
# #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
|
||||
string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
|
||||
string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
|
||||
set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
|
||||
endmacro()
|
||||
|
||||
##############################################################################
|
||||
|
||||
# Get the coverage data.
|
||||
file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
|
||||
message("GCDA files:")
|
||||
|
||||
# Get a list of all the object directories needed by gcov
|
||||
# (The directories the .gcda files and .o files are found in)
|
||||
# and run gcov on those.
|
||||
foreach(GCDA ${GCDA_FILES})
|
||||
message("Process: ${GCDA}")
|
||||
message("------------------------------------------------------------------------------")
|
||||
get_filename_component(GCDA_DIR ${GCDA} PATH)
|
||||
|
||||
#
|
||||
# The -p below refers to "Preserve path components",
|
||||
# This means that the generated gcov filename of a source file will
|
||||
# keep the original files entire filepath, but / is replaced with #.
|
||||
# Example:
|
||||
#
|
||||
# /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
|
||||
# ------------------------------------------------------------------------------
|
||||
# File '/path/to/project/root/subdir/the_file.c'
|
||||
# Lines executed:68.34% of 199
|
||||
# /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
|
||||
#
|
||||
# If -p is not specified then the file is named only "the_file.c.gcov"
|
||||
#
|
||||
execute_process(
|
||||
COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
|
||||
WORKING_DIRECTORY ${COV_PATH}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# TODO: Make these be absolute path
|
||||
file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
|
||||
|
||||
# Get only the filenames to use for filtering.
|
||||
#set(COVERAGE_SRCS_NAMES "")
|
||||
#foreach (COVSRC ${COVERAGE_SRCS})
|
||||
# get_filename_component(COVSRC_NAME ${COVSRC} NAME)
|
||||
# message("${COVSRC} -> ${COVSRC_NAME}")
|
||||
# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
|
||||
#endforeach()
|
||||
|
||||
#
|
||||
# Filter out all but the gcov files we want.
|
||||
#
|
||||
# We do this by comparing the list of COVERAGE_SRCS filepaths that the
|
||||
# user wants the coverage data for with the paths of the generated .gcov files,
|
||||
# so that we only keep the relevant gcov files.
|
||||
#
|
||||
# Example:
|
||||
# COVERAGE_SRCS =
|
||||
# /path/to/project/root/subdir/the_file.c
|
||||
#
|
||||
# ALL_GCOV_FILES =
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
|
||||
#
|
||||
# Result should be:
|
||||
# GCOV_FILES =
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
#
|
||||
set(GCOV_FILES "")
|
||||
#message("Look in coverage sources: ${COVERAGE_SRCS}")
|
||||
message("\nFilter out unwanted GCOV files:")
|
||||
message("===============================")
|
||||
|
||||
set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
|
||||
|
||||
foreach (GCOV_FILE ${ALL_GCOV_FILES})
|
||||
|
||||
#
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
# ->
|
||||
# /path/to/project/root/subdir/the_file.c
|
||||
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
|
||||
|
||||
# Is this in the list of source files?
|
||||
# TODO: We want to match against relative path filenames from the source file root...
|
||||
list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
|
||||
|
||||
if (NOT WAS_FOUND EQUAL -1)
|
||||
message("YES: ${GCOV_FILE}")
|
||||
list(APPEND GCOV_FILES ${GCOV_FILE})
|
||||
|
||||
# We remove it from the list, so we don't bother searching for it again.
|
||||
# Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
|
||||
# have coverage data generated from them (no lines are covered).
|
||||
list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
|
||||
else()
|
||||
message("NO: ${GCOV_FILE}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# TODO: Enable setting these
|
||||
set(JSON_SERVICE_NAME "travis-ci")
|
||||
set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
|
||||
|
||||
set(JSON_TEMPLATE
|
||||
"{
|
||||
\"service_name\": \"\@JSON_SERVICE_NAME\@\",
|
||||
\"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
|
||||
\"source_files\": \@JSON_GCOV_FILES\@
|
||||
}"
|
||||
)
|
||||
|
||||
set(SRC_FILE_TEMPLATE
|
||||
"{
|
||||
\"name\": \"\@GCOV_SRC_REL_PATH\@\",
|
||||
\"source\": \"\@GCOV_FILE_SOURCE\@\",
|
||||
\"coverage\": \@GCOV_FILE_COVERAGE\@
|
||||
}"
|
||||
)
|
||||
|
||||
message("\nGenerate JSON for files:")
|
||||
message("=========================")
|
||||
|
||||
set(JSON_GCOV_FILES "[")
|
||||
|
||||
# Read the GCOV files line by line and get the coverage data.
|
||||
foreach (GCOV_FILE ${GCOV_FILES})
|
||||
|
||||
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
|
||||
file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
|
||||
|
||||
# Loads the gcov file as a list of lines.
|
||||
file(STRINGS ${GCOV_FILE} GCOV_LINES)
|
||||
|
||||
# Instead of trying to parse the source from the
|
||||
# gcov file, simply read the file contents from the source file.
|
||||
# (Parsing it from the gcov is hard because C-code uses ; in many places
|
||||
# which also happens to be the same as the CMake list delimeter).
|
||||
file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
|
||||
|
||||
string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
# According to http://json.org/ these should be escaped as well.
|
||||
# Don't know how to do that in CMake however...
|
||||
#string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
#string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
#string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
|
||||
# We want a json array of coverage data as a single string
|
||||
# start building them from the contents of the .gcov
|
||||
set(GCOV_FILE_COVERAGE "[")
|
||||
|
||||
foreach (GCOV_LINE ${GCOV_LINES})
|
||||
# Example of what we're parsing:
|
||||
# Hitcount |Line | Source
|
||||
# " 8: 26: if (!allowed || (strlen(allowed) == 0))"
|
||||
string(REGEX REPLACE
|
||||
"^([^:]*):([^:]*):(.*)$"
|
||||
"\\1;\\2;\\3"
|
||||
RES
|
||||
"${GCOV_LINE}")
|
||||
|
||||
list(LENGTH RES RES_COUNT)
|
||||
if (RES_COUNT GREATER 2)
|
||||
list(GET RES 0 HITCOUNT)
|
||||
list(GET RES 1 LINE)
|
||||
list(GET RES 2 SOURCE)
|
||||
|
||||
string(STRIP ${HITCOUNT} HITCOUNT)
|
||||
string(STRIP ${LINE} LINE)
|
||||
|
||||
# Lines with 0 line numbers are metadata and can be ignored.
|
||||
if (NOT ${LINE} EQUAL 0)
|
||||
|
||||
# Translate the hitcount into valid JSON values.
|
||||
if (${HITCOUNT} STREQUAL "#####")
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
|
||||
elseif (${HITCOUNT} STREQUAL "-")
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
|
||||
else()
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
|
||||
endif()
|
||||
# TODO: Look for LCOV_EXCL_LINE in SOURCE to get rid of false positives.
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Failed to properly parse line --> ${GCOV_LINE}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Advanced way of removing the trailing comma in the JSON array.
|
||||
# "[1, 2, 3, " -> "[1, 2, 3"
|
||||
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
|
||||
|
||||
# Append the trailing ] to complete the JSON array.
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
|
||||
|
||||
# Generate the final JSON for this file.
|
||||
message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
|
||||
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
|
||||
|
||||
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
|
||||
endforeach()
|
||||
|
||||
# Loop through all files we couldn't find any coverage for
|
||||
# as well, and generate JSON for those as well with 0% coverage.
|
||||
foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
|
||||
|
||||
# Loads the source file as a list of lines.
|
||||
file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
|
||||
|
||||
set(GCOV_FILE_COVERAGE "[")
|
||||
set(GCOV_FILE_SOURCE "")
|
||||
|
||||
foreach (SOURCE ${SRC_LINES})
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
|
||||
|
||||
string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
|
||||
string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
|
||||
string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
|
||||
string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
|
||||
set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
|
||||
endforeach()
|
||||
|
||||
# Remove trailing comma, and complete JSON array with ]
|
||||
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
|
||||
|
||||
# Generate the final JSON for this file.
|
||||
message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
|
||||
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
|
||||
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
|
||||
endforeach()
|
||||
|
||||
# Get rid of trailing comma.
|
||||
string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
|
||||
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
|
||||
|
||||
# Generate the final complete JSON!
|
||||
message("Generate final JSON...")
|
||||
string(CONFIGURE ${JSON_TEMPLATE} JSON)
|
||||
|
||||
file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
|
||||
message("###########################################################################")
|
||||
message("Generated coveralls JSON containing coverage data:")
|
||||
message("${COVERALLS_OUTPUT_FILE}")
|
||||
message("###########################################################################")
|
||||
|
||||
17
cmake/JanssonConfig.cmake.in
Normal file
17
cmake/JanssonConfig.cmake.in
Normal file
@@ -0,0 +1,17 @@
|
||||
# - Config file for the jansson package
|
||||
# It defines the following variables
|
||||
# JANSSON_INCLUDE_DIRS - include directories for FooBar
|
||||
# JANSSON_LIBRARIES - libraries to link against
|
||||
|
||||
# Get the path of the current file.
|
||||
get_filename_component(JANSSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
|
||||
# Set the include directories.
|
||||
set(JANSSON_INCLUDE_DIRS "@JANSSON__INCLUDE_DIRS@")
|
||||
|
||||
# Include the project Targets file, this contains definitions for IMPORTED targets.
|
||||
include(${JANSSON_CMAKE_DIR}/JanssonTargets.cmake)
|
||||
|
||||
# IMPORTED targets from JanssonTargets.cmake
|
||||
set(JANSSON_LIBRARIES jansson)
|
||||
|
||||
11
cmake/JanssonConfigVersion.cmake.in
Normal file
11
cmake/JanssonConfigVersion.cmake.in
Normal file
@@ -0,0 +1,11 @@
|
||||
set(PACKAGE_VERSION "@JANSSON_DISPLAY_VERSION@")
|
||||
|
||||
# Check whether the requested PACKAGE_FIND_VERSION is compatible
|
||||
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -17,7 +17,9 @@
|
||||
#define JANSSON_CONFIG_H
|
||||
|
||||
/* Define this so that we can disable scattered automake configuration in source files */
|
||||
#ifndef JANSSON_USING_CMAKE
|
||||
#define JANSSON_USING_CMAKE
|
||||
#endif
|
||||
|
||||
/* Note: when using cmake, JSON_INTEGER_IS_LONG_LONG is not defined nor used,
|
||||
* as we will also check for __int64 etc types.
|
||||
@@ -58,5 +60,9 @@
|
||||
#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@
|
||||
|
||||
|
||||
/* Maximum recursion depth for parsing JSON input.
|
||||
This limits the depth of e.g. array-within-array constructions. */
|
||||
#define JSON_PARSER_MAX_DEPTH 2048
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,19 +31,23 @@
|
||||
# define uint32_t @JSON_UINT32@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT16_T 1
|
||||
#ifndef HAVE_UINT16_T
|
||||
# define uint16_t @JSON_UINT16@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT8_T 1
|
||||
#ifndef HAVE_UINT8_T
|
||||
# define uint8_t @JSON_UINT8@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_SSIZE_T 1
|
||||
|
||||
#ifndef HAVE_SSIZE_T
|
||||
# define ssize_t @JSON_SSIZE@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_SNPRINTF 1
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
# define snprintf @JSON_SNPRINTF@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_VSNPRINTF
|
||||
|
||||
#cmakedefine USE_URANDOM 1
|
||||
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
|
||||
|
||||
#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@
|
||||
19
configure.ac
19
configure.ac
@@ -1,10 +1,11 @@
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.6], [petri@digip.org])
|
||||
AC_INIT([jansson], [2.10], [petri@digip.org])
|
||||
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/value.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_HEADERS([jansson_private_config.h])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
@@ -19,6 +20,8 @@ AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/sta
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT8_T
|
||||
AC_TYPE_LONG_LONG_INT
|
||||
|
||||
AC_C_INLINE
|
||||
@@ -89,6 +92,18 @@ AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1],
|
||||
[Define to 1 if CryptGenRandom should be used for seeding the hash function])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([initial-hashtable-order],
|
||||
[AS_HELP_STRING([--enable-initial-hashtable-order=VAL],
|
||||
[Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.])],
|
||||
[initial_hashtable_order=$enableval], [initial_hashtable_order=3])
|
||||
AC_DEFINE_UNQUOTED([INITIAL_HASHTABLE_ORDER], [$initial_hashtable_order],
|
||||
[Number of buckets new object hashtables contain is 2 raised to this power. E.g. 3 -> 2^3 = 8.])
|
||||
|
||||
if test x$GCC = xyes; then
|
||||
AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement"
|
||||
fi
|
||||
AC_SUBST([AM_CFLAGS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
jansson.pc
|
||||
Makefile
|
||||
|
||||
370
doc/apiref.rst
370
doc/apiref.rst
@@ -52,7 +52,7 @@ the library:
|
||||
``JANSSON_VERSION_HEX``
|
||||
A 3-byte hexadecimal representation of the version, e.g.
|
||||
``0x010201`` for version 1.2.1 and ``0x010300`` for version 1.3.
|
||||
This is useful in numeric comparisions, e.g.::
|
||||
This is useful in numeric comparisons, e.g.::
|
||||
|
||||
#if JANSSON_VERSION_HEX >= 0x010300
|
||||
/* Code specific to version 1.3 and above */
|
||||
@@ -90,9 +90,6 @@ also cause errors.
|
||||
Type
|
||||
----
|
||||
|
||||
The type of a JSON value is queried and tested using the following
|
||||
functions:
|
||||
|
||||
.. type:: enum json_type
|
||||
|
||||
The type of a JSON value. The following members are defined:
|
||||
@@ -150,6 +147,13 @@ functions:
|
||||
Returns true for types ``JSON_TRUE`` and ``JSON_FALSE``, and false
|
||||
for values of other types and for *NULL*.
|
||||
|
||||
.. function:: json_boolean_value(const json_t *json)
|
||||
|
||||
Alias of :func:`json_is_true()`, i.e. returns 1 for ``JSON_TRUE``
|
||||
and 0 otherwise.
|
||||
|
||||
.. versionadded:: 2.7
|
||||
|
||||
|
||||
.. _apiref-reference-count:
|
||||
|
||||
@@ -164,8 +168,6 @@ no longer needed, the reference count is decremented. When the
|
||||
reference count drops to zero, there are no references left, and the
|
||||
value can be destroyed.
|
||||
|
||||
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 *NULL*.
|
||||
@@ -251,6 +253,26 @@ other. Moreover, trying to encode the values with any of the encoding
|
||||
functions will fail. The encoder detects circular references and
|
||||
returns an error status.
|
||||
|
||||
Scope Dereferencing
|
||||
-------------------
|
||||
|
||||
.. versionadded:: 2.9
|
||||
|
||||
It is possible to use the ``json_auto_t`` type to automatically
|
||||
dereference a value at the end of a scope. For example::
|
||||
|
||||
void function(void) {
|
||||
json_auto_t *value = NULL;
|
||||
value = json_string("foo");
|
||||
/* json_decref(value) is automatically called. */
|
||||
}
|
||||
|
||||
This feature is only available on GCC and Clang. So if your project
|
||||
has a portability requirement for other compilers, you should avoid
|
||||
this feature.
|
||||
|
||||
Additionally, as always, care should be taken when passing values to
|
||||
functions that steal references.
|
||||
|
||||
True, False and Null
|
||||
====================
|
||||
@@ -292,17 +314,23 @@ String
|
||||
======
|
||||
|
||||
Jansson uses UTF-8 as the character encoding. All JSON strings must be
|
||||
valid UTF-8 (or ASCII, as it's a subset of UTF-8). Normal null
|
||||
terminated C strings are used, so JSON strings may not contain
|
||||
embedded null characters. All other Unicode codepoints U+0001 through
|
||||
U+10FFFF are allowed.
|
||||
valid UTF-8 (or ASCII, as it's a subset of UTF-8). All Unicode
|
||||
codepoints U+0000 through U+10FFFF are allowed, but you must use
|
||||
length-aware functions if you wish to embed null bytes in strings.
|
||||
|
||||
.. function:: json_t *json_string(const char *value)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new JSON string, or *NULL* on error. *value* must be a
|
||||
valid UTF-8 encoded Unicode string.
|
||||
valid null terminated UTF-8 encoded Unicode string.
|
||||
|
||||
.. function:: json_t *json_stringn(const char *value, size_t len)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Like :func:`json_string`, but with explicit length, so *value* may
|
||||
contain null characters or not be null terminated.
|
||||
|
||||
.. function:: json_t *json_string_nocheck(const char *value)
|
||||
|
||||
@@ -312,28 +340,50 @@ U+10FFFF are allowed.
|
||||
UTF-8. Use this function only if you are certain that this really
|
||||
is the case (e.g. you have already checked it by other means).
|
||||
|
||||
.. function:: json_t *json_stringn_nocheck(const char *value, size_t len)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Like :func:`json_string_nocheck`, but with explicit length, so
|
||||
*value* may contain null characters or not be null terminated.
|
||||
|
||||
.. function:: const char *json_string_value(const json_t *string)
|
||||
|
||||
Returns the associated value of *string* as a null terminated UTF-8
|
||||
encoded string, or *NULL* if *string* is not a JSON string.
|
||||
|
||||
The retuned value is read-only and must not be modified or freed by
|
||||
The returned value is read-only and must not be modified or freed by
|
||||
the user. It is valid as long as *string* exists, i.e. as long as
|
||||
its reference count has not dropped to zero.
|
||||
|
||||
.. function:: int json_string_set(const json_t *string, const char *value)
|
||||
.. function:: size_t json_string_length(const json_t *string)
|
||||
|
||||
Returns the length of *string* in its UTF-8 presentation, or zero
|
||||
if *string* is not a JSON string.
|
||||
|
||||
.. function:: int json_string_set(json_t *string, const char *value)
|
||||
|
||||
Sets the associated value of *string* to *value*. *value* must be a
|
||||
valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on
|
||||
error.
|
||||
|
||||
.. function:: int json_string_set_nocheck(const json_t *string, const char *value)
|
||||
.. function:: int json_string_setn(json_t *string, const char *value, size_t len)
|
||||
|
||||
Like :func:`json_string_set`, but with explicit length, so *value*
|
||||
may contain null characters or not be null terminated.
|
||||
|
||||
.. function:: int json_string_set_nocheck(json_t *string, const char *value)
|
||||
|
||||
Like :func:`json_string_set`, but doesn't check that *value* is
|
||||
valid UTF-8. Use this function only if you are certain that this
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. function:: int json_string_setn_nocheck(json_t *string, const char *value, size_t len)
|
||||
|
||||
Like :func:`json_string_set_nocheck`, but with explicit length,
|
||||
so *value* may contain null characters or not be null terminated.
|
||||
|
||||
|
||||
Number
|
||||
======
|
||||
@@ -372,7 +422,7 @@ information, see :ref:`rfc-conformance`.
|
||||
specifier that corresponds to :type:`json_int_t`, without the
|
||||
leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro
|
||||
is required because the actual type of :type:`json_int_t` can be
|
||||
either ``long`` or ``long long``, and :func:`printf()` reuiqres
|
||||
either ``long`` or ``long long``, and :func:`printf()` requires
|
||||
different length modifiers for the two.
|
||||
|
||||
Example::
|
||||
@@ -413,9 +463,6 @@ information, see :ref:`rfc-conformance`.
|
||||
Sets the associated value of *real* to *value*. Returns 0 on
|
||||
success and -1 if *real* is not a JSON real.
|
||||
|
||||
In addition to the functions above, there's a common query function
|
||||
for integers and reals:
|
||||
|
||||
.. function:: double json_number_value(const json_t *json)
|
||||
|
||||
Returns the associated value of the JSON integer or JSON real
|
||||
@@ -495,7 +542,7 @@ A JSON array is an ordered collection of other JSON values.
|
||||
|
||||
.. function:: int json_array_clear(json_t *array)
|
||||
|
||||
Removes all elements from *array*. Returns 0 on sucess and -1 on
|
||||
Removes all elements from *array*. Returns 0 on success and -1 on
|
||||
error. The reference count of all removed values are decremented.
|
||||
|
||||
.. function:: int json_array_extend(json_t *array, json_t *other_array)
|
||||
@@ -503,9 +550,6 @@ A JSON array is an ordered collection of other JSON values.
|
||||
Appends all elements in *other_array* to the end of *array*.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
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
|
||||
@@ -527,8 +571,7 @@ in an array.
|
||||
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.
|
||||
away the complexity, and makes for more concise and readable code.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
@@ -539,6 +582,9 @@ Object
|
||||
A JSON object is a dictionary of key-value pairs, where the key is a
|
||||
Unicode string and the value is any JSON value.
|
||||
|
||||
Even though null bytes are allowed in string values, they are not
|
||||
allowed in object keys.
|
||||
|
||||
.. function:: json_t *json_object(void)
|
||||
|
||||
.. refcounting:: new
|
||||
@@ -618,9 +664,6 @@ Unicode string and the value is any JSON value.
|
||||
|
||||
.. 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
|
||||
@@ -636,22 +679,35 @@ in an object.
|
||||
/* block of code that uses key and value */
|
||||
}
|
||||
|
||||
The items are not returned in any particular order.
|
||||
The items are returned in the order they were inserted to the
|
||||
object.
|
||||
|
||||
**Note:** It's not safe to call ``json_object_del(object, key)``
|
||||
during iteration. If you need to, use
|
||||
:func:`json_object_foreach_safe` instead.
|
||||
|
||||
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.
|
||||
away the complexity behind iteration, and makes for more concise and
|
||||
readable 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:: json_object_foreach_safe(object, tmp, key, value)
|
||||
|
||||
Like :func:`json_object_foreach()`, but it's safe to call
|
||||
``json_object_del(object, key)`` during iteration. You need to pass
|
||||
an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
|
||||
The following functions can be used to iterate through all key-value
|
||||
pairs in an object. The items are returned in the order they were
|
||||
inserted to the object.
|
||||
|
||||
.. function:: void *json_object_iter(json_t *object)
|
||||
|
||||
@@ -698,32 +754,30 @@ sorting due to the internal hashtable implementation.
|
||||
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`.
|
||||
implement :func:`json_object_foreach`. Example::
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
void *iter = json_object_iter(obj);
|
||||
while(iter)
|
||||
{
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
/* use key and value ... */
|
||||
iter = json_object_iter_next(obj, iter);
|
||||
}
|
||||
|
||||
.. 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)
|
||||
{
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
/* use key and value ... */
|
||||
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
|
||||
If *seed* is 0, Jansson generates the seed itself 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)
|
||||
@@ -762,7 +816,7 @@ this struct.
|
||||
.. member:: char source[]
|
||||
|
||||
Source of the error. This can be (a part of) the file name or a
|
||||
special identifier in angle brackers (e.g. ``<string>``).
|
||||
special identifier in angle brackets (e.g. ``<string>``).
|
||||
|
||||
.. member:: int line
|
||||
|
||||
@@ -774,7 +828,7 @@ this struct.
|
||||
*character column*, not the byte column, i.e. a multibyte UTF-8
|
||||
character counts as one column.
|
||||
|
||||
.. member:: size_t position
|
||||
.. member:: int position
|
||||
|
||||
The position in bytes from the start of the input. This is
|
||||
useful for debugging Unicode encoding problems.
|
||||
@@ -828,6 +882,12 @@ can be ORed together to obtain *flags*.
|
||||
output. If ``JSON_INDENT`` is not used or *n* is 0, no newlines are
|
||||
inserted between array and object items.
|
||||
|
||||
The ``JSON_MAX_INDENT`` constant defines the maximum indentation
|
||||
that can be used, and its value is 31.
|
||||
|
||||
.. versionchanged:: 2.7
|
||||
Added ``JSON_MAX_INDENT``.
|
||||
|
||||
``JSON_COMPACT``
|
||||
This flag enables a compact representation, i.e. sets the separator
|
||||
between array and object items to ``","`` and between object keys
|
||||
@@ -836,7 +896,7 @@ can be ORed together to obtain *flags*.
|
||||
|
||||
``JSON_ENSURE_ASCII``
|
||||
If this flag is used, the output is guaranteed to consist only of
|
||||
ASCII characters. This is achived by escaping all Unicode
|
||||
ASCII characters. This is achieved by escaping all Unicode
|
||||
characters outside the ASCII range.
|
||||
|
||||
``JSON_SORT_KEYS``
|
||||
@@ -845,19 +905,22 @@ can be ORed together to obtain *flags*.
|
||||
compared.
|
||||
|
||||
``JSON_PRESERVE_ORDER``
|
||||
If this flag is used, object keys in the output are sorted into the
|
||||
same order in which they were first inserted to the object. For
|
||||
example, decoding a JSON text and then encoding with this flag
|
||||
preserves the order of object keys.
|
||||
**Deprecated since version 2.8:** Order of object keys
|
||||
is always preserved.
|
||||
|
||||
Prior to version 2.8: If this flag is used, object keys in the
|
||||
output are sorted into the same order in which they were first
|
||||
inserted to the object. For 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.
|
||||
*json* 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
|
||||
it's generally discouraged as it violates strict compatibility with
|
||||
:rfc:`4627`. If you use this flag, don't expect interoperability
|
||||
with other JSON systems.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
@@ -867,26 +930,81 @@ can be ORed together to obtain *flags*.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
The following functions perform the actual JSON encoding. The result
|
||||
is in UTF-8.
|
||||
``JSON_REAL_PRECISION(n)``
|
||||
Output all real numbers with at most *n* digits of precision. The
|
||||
valid range for *n* is between 0 and 31 (inclusive), and other
|
||||
values result in an undefined behavior.
|
||||
|
||||
.. function:: char *json_dumps(const json_t *root, size_t flags)
|
||||
By default, the precision is 17, to correctly and losslessly encode
|
||||
all IEEE 754 double precision floating point numbers.
|
||||
|
||||
Returns the JSON representation of *root* as a string, or *NULL* on
|
||||
.. versionadded:: 2.7
|
||||
|
||||
``JSON_EMBED``
|
||||
If this flag is used, the opening and closing characters of the top-level
|
||||
array ('[', ']') or object ('{', '}') are omitted during encoding. This
|
||||
flag is useful when concatenating multiple arrays or objects into a stream.
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
These functions output UTF-8:
|
||||
|
||||
.. function:: char *json_dumps(const json_t *json, size_t flags)
|
||||
|
||||
Returns the JSON representation of *json* as a string, or *NULL* on
|
||||
error. *flags* is described above. The return value must be freed
|
||||
by the caller using :func:`free()`.
|
||||
|
||||
.. function:: int json_dumpf(const json_t *root, FILE *output, size_t flags)
|
||||
.. function:: size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
|
||||
|
||||
Write the JSON representation of *root* to the stream *output*.
|
||||
Writes the JSON representation of *json* to the *buffer* of
|
||||
*size* bytes. Returns the number of bytes that would be written
|
||||
or 0 on error. *flags* is described above. *buffer* is not
|
||||
null-terminated.
|
||||
|
||||
This function never writes more than *size* bytes. If the return
|
||||
value is greater than *size*, the contents of the *buffer* are
|
||||
undefined. This behavior enables you to specify a NULL *buffer*
|
||||
to determine the length of the encoding. For example::
|
||||
|
||||
size_t size = json_dumpb(json, NULL, 0, 0);
|
||||
if (size == 0)
|
||||
return -1;
|
||||
|
||||
char *buf = alloca(size);
|
||||
|
||||
size = json_dumpb(json, buf, size, 0);
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
.. function:: int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
|
||||
Write the JSON representation of *json* to the stream *output*.
|
||||
*flags* is described above. Returns 0 on success and -1 on error.
|
||||
If an error occurs, something may have already been written to
|
||||
*output*. In this case, the output is undefined and most likely not
|
||||
valid JSON.
|
||||
|
||||
.. function:: int json_dumpfd(const json_t *json, int output, size_t flags)
|
||||
|
||||
Write the JSON representation of *json* to the stream *output*.
|
||||
*flags* is described above. Returns 0 on success and -1 on error.
|
||||
If an error occurs, something may have already been written to
|
||||
*output*. In this case, the output is undefined and most likely not
|
||||
valid JSON.
|
||||
|
||||
It is important to note that this function can only succeed on stream
|
||||
file descriptors (such as SOCK_STREAM). Using this function on a
|
||||
non-stream file descriptor will result in undefined behavior. For
|
||||
non-stream file descriptors, see instead :func:`json_dumpb()`.
|
||||
|
||||
This function requires POSIX and fails on all non-POSIX systems.
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
.. function:: int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
|
||||
Write the JSON representation of *root* to the file *path*. If
|
||||
Write the JSON representation of *json* to the file *path*. If
|
||||
*path* already exists, it is overwritten. *flags* is described
|
||||
above. Returns 0 on success and -1 on error.
|
||||
|
||||
@@ -909,7 +1027,7 @@ is in UTF-8.
|
||||
.. 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.
|
||||
representation of *json* each time. *flags* is described above.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
@@ -939,7 +1057,7 @@ 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
|
||||
occurrence of each key ends up in the result. Key equivalence is
|
||||
checked byte-by-byte, without special Unicode comparison
|
||||
algorithms.
|
||||
|
||||
@@ -950,8 +1068,8 @@ macros can be ORed together to obtain *flags*.
|
||||
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
|
||||
it's generally discouraged as it violates strict compatibility with
|
||||
:rfc:`4627`. If you use this flag, don't expect interoperability
|
||||
with other JSON systems.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
@@ -984,6 +1102,19 @@ macros can be ORed together to obtain *flags*.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
``JSON_ALLOW_NUL``
|
||||
Allow ``\u0000`` escape inside string values. This is a safety
|
||||
measure; If you know your input can contain null bytes, use this
|
||||
flag. If you don't use this flag, you don't have to worry about null
|
||||
bytes inside strings unless you explicitly create themselves by
|
||||
using e.g. :func:`json_stringn()` or ``s#`` format specifier for
|
||||
:func:`json_pack()`.
|
||||
|
||||
Object keys cannot have embedded null bytes even if this flag is
|
||||
used.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
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
|
||||
@@ -996,8 +1127,6 @@ its ``position`` field. This is especially useful when using
|
||||
|
||||
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
|
||||
@@ -1028,7 +1157,7 @@ The following functions perform the actual JSON decoding.
|
||||
above.
|
||||
|
||||
This function will start reading the input from whatever position
|
||||
the input file was, without attempting to seek first. If an error
|
||||
the input file was in, 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
|
||||
@@ -1037,6 +1166,35 @@ The following functions perform the actual JSON decoding.
|
||||
multiple times, if the input consists of consecutive JSON texts,
|
||||
possibly separated by whitespace.
|
||||
|
||||
.. function:: json_t *json_loadfd(int input, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
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 described
|
||||
above.
|
||||
|
||||
This function will start reading the input from whatever position
|
||||
the input file descriptor was in, 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
|
||||
descriptor's position will be at the first character after the last
|
||||
``]`` or ``}`` in the JSON input. This allows calling
|
||||
:func:`json_loadfd()` on the same file descriptor multiple times,
|
||||
if the input consists of consecutive JSON texts, possibly separated
|
||||
by whitespace.
|
||||
|
||||
It is important to note that this function can only succeed on stream
|
||||
file descriptors (such as SOCK_STREAM). Using this function on a
|
||||
non-stream file descriptor will result in undefined behavior. For
|
||||
non-stream file descriptors, see instead :func:`json_loadb()`.
|
||||
|
||||
This function requires POSIX and fails on all non-POSIX systems.
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
@@ -1103,13 +1261,24 @@ 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.
|
||||
Convert a null terminated UTF-8 string to a JSON string.
|
||||
|
||||
``s?`` (string) [const char \*]
|
||||
Like ``s``, but if the argument is *NULL*, output a JSON null
|
||||
value.
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
``s#`` (string) [const char \*, int]
|
||||
Convert a UTF-8 buffer of a given length to a JSON string.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
``s%`` (string) [const char \*, size_t]
|
||||
Like ``s#`` but the length argument is of type :type:`size_t`.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
``+`` [const char \*]
|
||||
Like ``s``, but concatenate to the previous string. Only valid
|
||||
after ``s``, ``s#``, ``+`` or ``+#``.
|
||||
@@ -1122,6 +1291,11 @@ arguments.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
|
||||
``+%`` (string) [const char \*, size_t]
|
||||
Like ``+#`` but the length argument is of type :type:`size_t`.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
``n`` (null)
|
||||
Output a JSON null value. No argument is consumed.
|
||||
|
||||
@@ -1149,6 +1323,12 @@ arguments.
|
||||
keep the reference for the JSON value consumed by ``O`` to
|
||||
yourself.
|
||||
|
||||
``o?``, ``O?`` (any value) [json_t \*]
|
||||
Like ``o`` and ``O?``, respectively, but if the argument is
|
||||
*NULL*, output a JSON null value.
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
``[fmt]`` (array)
|
||||
Build an array with contents from the inner format string. ``fmt``
|
||||
may contain objects and arrays, i.e. recursive value building is
|
||||
@@ -1164,8 +1344,6 @@ arguments.
|
||||
|
||||
Whitespace, ``:`` and ``,`` are ignored.
|
||||
|
||||
The following functions compose the value building API:
|
||||
|
||||
.. function:: json_t *json_pack(const char *fmt, ...)
|
||||
|
||||
.. refcounting:: new
|
||||
@@ -1202,11 +1380,11 @@ More examples::
|
||||
/* Build the JSON array [[1, 2], {"cool": true}] */
|
||||
json_pack("[[i,i],{s:b}]", 1, 2, "cool", 1);
|
||||
|
||||
/* Build a string from a non-NUL terminated buffer */
|
||||
/* Build a string from a non-null terminated buffer */
|
||||
char buffer[4] = {'t', 'e', 's', 't'};
|
||||
json_pack("s#", buffer, 4);
|
||||
|
||||
/* Concatentate strings together to build the JSON string "foobarbaz" */
|
||||
/* Concatenate strings together to build the JSON string "foobarbaz" */
|
||||
json_pack("s++", "foo", "bar", "baz");
|
||||
|
||||
|
||||
@@ -1231,11 +1409,17 @@ 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
|
||||
Convert a JSON string to a pointer to a null terminated UTF-8
|
||||
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.
|
||||
|
||||
``s%`` (string) [const char \*, size_t \*]
|
||||
Convert a JSON string to a pointer to a null terminated UTF-8
|
||||
string and its length.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
``n`` (null)
|
||||
Expect a JSON null value. Nothing is extracted.
|
||||
|
||||
@@ -1264,7 +1448,7 @@ type whose address should be passed.
|
||||
``[fmt]`` (array)
|
||||
Convert each item in the JSON array according to the inner format
|
||||
string. ``fmt`` may contain objects and arrays, i.e. recursive
|
||||
value extraction is supporetd.
|
||||
value extraction is supported.
|
||||
|
||||
``{fmt}`` (object)
|
||||
Convert each item in the JSON object according to the inner format
|
||||
@@ -1276,7 +1460,7 @@ type whose address should be passed.
|
||||
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.
|
||||
value extraction is supported.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
Any ``s`` representing a key may be suffixed with a ``?`` to
|
||||
@@ -1299,8 +1483,6 @@ type whose address should be passed.
|
||||
|
||||
Whitespace, ``:`` and ``,`` are ignored.
|
||||
|
||||
The following functions compose the parsing and validation API:
|
||||
|
||||
.. function:: int json_unpack(json_t *root, const char *fmt, ...)
|
||||
|
||||
Validate and unpack the JSON value *root* according to the format
|
||||
@@ -1366,7 +1548,7 @@ Examples::
|
||||
/* returns -1 for failed validation */
|
||||
|
||||
/* root is an empty JSON object */
|
||||
int myint = 0, myint2 = 0;
|
||||
int myint = 0, myint2 = 0, myint3 = 0;
|
||||
json_unpack(root, "{s?i, s?[ii]}",
|
||||
"foo", &myint1,
|
||||
"bar", &myint2, &myint3);
|
||||
@@ -1402,13 +1584,10 @@ only if they are exactly the same value, but also if they have equal
|
||||
if their types are equal. (Because these values are singletons,
|
||||
their equality can actually be tested with ``==``.)
|
||||
|
||||
The following function can be used to test whether two JSON values are
|
||||
equal.
|
||||
|
||||
.. function:: int json_equal(json_t *value1, json_t *value2)
|
||||
|
||||
Returns 1 if *value1* and *value2* are equal, as defined above.
|
||||
Returns 0 if they are inequal or one or both of the pointers are
|
||||
Returns 0 if they are unequal or one or both of the pointers are
|
||||
*NULL*.
|
||||
|
||||
|
||||
@@ -1427,6 +1606,8 @@ the same child values in the copied value. Deep copying makes a fresh
|
||||
copy of the child values, too. Moreover, all the child values are deep
|
||||
copied in a recursive fashion.
|
||||
|
||||
Copying objects preserves the insertion order of keys.
|
||||
|
||||
.. function:: json_t *json_copy(json_t *value)
|
||||
|
||||
.. refcounting:: new
|
||||
@@ -1470,6 +1651,13 @@ behavior is needed.
|
||||
Jansson's API functions to ensure that all memory operations use
|
||||
the same functions.
|
||||
|
||||
.. function:: void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
|
||||
|
||||
Fetch the current malloc_fn and free_fn used. Either parameter
|
||||
may be NULL.
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
**Examples:**
|
||||
|
||||
Circumvent problems with different CRT heaps on Windows by using
|
||||
@@ -1482,7 +1670,7 @@ operations::
|
||||
|
||||
json_set_alloc_funcs(GC_malloc, GC_free);
|
||||
|
||||
.. _Boehm's conservative garbage collector: http://www.hpl.hp.com/personal/Hans_Boehm/gc/
|
||||
.. _Boehm's conservative garbage collector: http://www.hboehm.info/gc/
|
||||
|
||||
Allow storing sensitive data (e.g. passwords or encryption keys) in
|
||||
JSON structures by zeroing all memory when freed::
|
||||
|
||||
@@ -41,14 +41,14 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Jansson'
|
||||
copyright = u'2009-2013, Petri Lehtinen'
|
||||
copyright = u'2009-2016, Petri Lehtinen'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.6'
|
||||
version = '2.10'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
||||
@@ -19,15 +19,11 @@ Strings
|
||||
=======
|
||||
|
||||
JSON strings are mapped to C-style null-terminated character arrays,
|
||||
and UTF-8 encoding is used internally. Strings may not contain
|
||||
embedded null characters, not even escaped ones.
|
||||
and UTF-8 encoding is used internally.
|
||||
|
||||
For example, trying to decode the following JSON text leads to a parse
|
||||
error::
|
||||
|
||||
["this string contains the null character: \u0000"]
|
||||
|
||||
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
|
||||
All Unicode codepoints U+0000 through U+10FFFF are allowed in string
|
||||
values. However, U+0000 is not allowed in object keys because of API
|
||||
restrictions.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
@@ -112,3 +108,13 @@ types, ``long double``, etc. Obviously, shorter types like ``short``,
|
||||
are implicitly handled via the ordinary C type coercion rules (subject
|
||||
to overflow semantics). Also, no support or hooks are provided for any
|
||||
supplemental "bignum" type add-on packages.
|
||||
|
||||
Depth of nested values
|
||||
----------------------
|
||||
|
||||
To avoid stack exhaustion, Jansson currently limits the nesting depth
|
||||
for arrays and objects to a certain value (default: 2048), defined as
|
||||
a macro ``JSON_PARSER_MAX_DEPTH`` within ``jansson_config.h``.
|
||||
|
||||
The limit is allowed to be set by the RFC; there is no recommended value
|
||||
or required minimum depth to be supported.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<description of the json_object function>
|
||||
|
||||
:copyright: Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
:copyright: Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
:license: MIT, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -55,5 +55,6 @@ def setup(app):
|
||||
app.add_node(refcounting,
|
||||
html=(html_visit, html_depart),
|
||||
latex=(visit, depart),
|
||||
text=(visit, depart))
|
||||
text=(visit, depart),
|
||||
man=(visit, depart))
|
||||
app.add_directive('refcounting', refcounting_directive, 0, (1, 0, 0))
|
||||
|
||||
@@ -30,8 +30,7 @@ compiling and installing is extremely simple::
|
||||
|
||||
To change the destination directory (``/usr/local`` by default), use
|
||||
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
|
||||
--help`` for the list of all possible installation options. (There are
|
||||
no options to customize the resulting Jansson binary.)
|
||||
--help`` for the list of all possible configuration options.
|
||||
|
||||
The command ``make check`` runs the test suite distributed with
|
||||
Jansson. This step is not strictly necessary, but it may find possible
|
||||
@@ -44,7 +43,7 @@ version control. To create the script, the build system needs to be
|
||||
bootstrapped. There are many ways to do this, but the easiest one is
|
||||
to use ``autoreconf``::
|
||||
|
||||
autoreconf -vi
|
||||
autoreconf -fi
|
||||
|
||||
This command creates the ``./configure`` script, which can then be
|
||||
used as described above.
|
||||
@@ -83,10 +82,10 @@ Generating make files on unix:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. # or `ccmake ..` for a GUI.
|
||||
cmake .. # or ccmake .. for a GUI.
|
||||
|
||||
Then to build::
|
||||
|
||||
|
||||
make
|
||||
make check
|
||||
make install
|
||||
@@ -107,7 +106,7 @@ Creating Visual Studio project files from the command line:
|
||||
You will now have a *Visual Studio Solution* in your build directory.
|
||||
To run the unit tests build the ``RUN_TESTS`` project.
|
||||
|
||||
If you prefer a GUI the ``cmake`` line in the above example can
|
||||
If you prefer a GUI the ``cmake`` line in the above example can
|
||||
be replaced with::
|
||||
|
||||
cmake-gui ..
|
||||
@@ -117,7 +116,7 @@ for CMake_ simply run::
|
||||
|
||||
cmake
|
||||
|
||||
To list available CMake_ settings (and what they are currently set to)
|
||||
To list available CMake_ settings (and what they are currently set to)
|
||||
for the project, run::
|
||||
|
||||
cmake -LH ..
|
||||
@@ -125,7 +124,7 @@ for the project, run::
|
||||
Mac OSX (Xcode)
|
||||
^^^^^^^^^^^^^^^
|
||||
If you prefer using Xcode instead of make files on OSX,
|
||||
do the following. (Use the same steps as
|
||||
do the following. (Use the same steps as
|
||||
for :ref:`Unix <build-cmake-unix>`)::
|
||||
|
||||
...
|
||||
@@ -140,12 +139,12 @@ By default the CMake_ project will generate build files for building the
|
||||
static library. To build the shared version use::
|
||||
|
||||
...
|
||||
cmake -DBUILD_SHARED=1 ..
|
||||
cmake -DJANSSON_BUILD_SHARED_LIBS=1 ..
|
||||
|
||||
Changing install directory (same as autoconf --prefix)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Just as with the autoconf_ project you can change the destination directory
|
||||
for ``make install``. The equivalent for autoconfs ``./configure --prefix``
|
||||
for ``make install``. The equivalent for autoconfs ``./configure --prefix``
|
||||
in CMake_ is::
|
||||
|
||||
...
|
||||
@@ -154,6 +153,7 @@ in CMake_ is::
|
||||
|
||||
.. _CMake: http://www.cmake.org
|
||||
|
||||
|
||||
Android
|
||||
-------
|
||||
|
||||
@@ -162,17 +162,6 @@ 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
|
||||
-------------
|
||||
|
||||
@@ -230,7 +219,9 @@ link the program as follows::
|
||||
|
||||
cc -o prog prog.c -ljansson
|
||||
|
||||
Starting from version 1.2, there's also support for pkg-config_::
|
||||
Starting from version 1.2, there's also support for pkg-config_:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cc -o prog prog.c `pkg-config --cflags --libs jansson`
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -256,7 +256,9 @@ For a detailed explanation of reference counting in Jansson, see
|
||||
:ref:`apiref-reference-count` in :ref:`apiref`.
|
||||
|
||||
The program's ready, let's test it and view the latest commits in
|
||||
Jansson's repository::
|
||||
Jansson's repository:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ ./github_commits akheron jansson
|
||||
1581f26a Merge branch '2.3'
|
||||
|
||||
4
examples/README.rst
Normal file
4
examples/README.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
Jansson examples
|
||||
================
|
||||
|
||||
This directory contains simple example programs that use Jansson.
|
||||
203
examples/simple_parse.c
Normal file
203
examples/simple_parse.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Simple example of parsing and printing JSON using jansson.
|
||||
*
|
||||
* SYNOPSIS:
|
||||
* $ examples/simple_parse
|
||||
* Type some JSON > [true, false, null, 1, 0.0, -0.0, "", {"name": "barney"}]
|
||||
* JSON Array of 8 elements:
|
||||
* JSON True
|
||||
* JSON False
|
||||
* JSON Null
|
||||
* JSON Integer: "1"
|
||||
* JSON Real: 0.000000
|
||||
* JSON Real: -0.000000
|
||||
* JSON String: ""
|
||||
* JSON Object of 1 pair:
|
||||
* JSON Key: "name"
|
||||
* JSON String: "barney"
|
||||
*
|
||||
* Copyright (c) 2014 Robert Poor <rdpoor@gmail.com>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <jansson.h>
|
||||
|
||||
/* forward refs */
|
||||
void print_json(json_t *root);
|
||||
void print_json_aux(json_t *element, int indent);
|
||||
void print_json_indent(int indent);
|
||||
const char *json_plural(int count);
|
||||
void print_json_object(json_t *element, int indent);
|
||||
void print_json_array(json_t *element, int indent);
|
||||
void print_json_string(json_t *element, int indent);
|
||||
void print_json_integer(json_t *element, int indent);
|
||||
void print_json_real(json_t *element, int indent);
|
||||
void print_json_true(json_t *element, int indent);
|
||||
void print_json_false(json_t *element, int indent);
|
||||
void print_json_null(json_t *element, int indent);
|
||||
|
||||
void print_json(json_t *root) {
|
||||
print_json_aux(root, 0);
|
||||
}
|
||||
|
||||
void print_json_aux(json_t *element, int indent) {
|
||||
switch (json_typeof(element)) {
|
||||
case JSON_OBJECT:
|
||||
print_json_object(element, indent);
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
print_json_array(element, indent);
|
||||
break;
|
||||
case JSON_STRING:
|
||||
print_json_string(element, indent);
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
print_json_integer(element, indent);
|
||||
break;
|
||||
case JSON_REAL:
|
||||
print_json_real(element, indent);
|
||||
break;
|
||||
case JSON_TRUE:
|
||||
print_json_true(element, indent);
|
||||
break;
|
||||
case JSON_FALSE:
|
||||
print_json_false(element, indent);
|
||||
break;
|
||||
case JSON_NULL:
|
||||
print_json_null(element, indent);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unrecognized JSON type %d\n", json_typeof(element));
|
||||
}
|
||||
}
|
||||
|
||||
void print_json_indent(int indent) {
|
||||
int i;
|
||||
for (i = 0; i < indent; i++) { putchar(' '); }
|
||||
}
|
||||
|
||||
const char *json_plural(int count) {
|
||||
return count == 1 ? "" : "s";
|
||||
}
|
||||
|
||||
void print_json_object(json_t *element, int indent) {
|
||||
size_t size;
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
print_json_indent(indent);
|
||||
size = json_object_size(element);
|
||||
|
||||
printf("JSON Object of %ld pair%s:\n", size, json_plural(size));
|
||||
json_object_foreach(element, key, value) {
|
||||
print_json_indent(indent + 2);
|
||||
printf("JSON Key: \"%s\"\n", key);
|
||||
print_json_aux(value, indent + 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void print_json_array(json_t *element, int indent) {
|
||||
size_t i;
|
||||
size_t size = json_array_size(element);
|
||||
print_json_indent(indent);
|
||||
|
||||
printf("JSON Array of %ld element%s:\n", size, json_plural(size));
|
||||
for (i = 0; i < size; i++) {
|
||||
print_json_aux(json_array_get(element, i), indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void print_json_string(json_t *element, int indent) {
|
||||
print_json_indent(indent);
|
||||
printf("JSON String: \"%s\"\n", json_string_value(element));
|
||||
}
|
||||
|
||||
void print_json_integer(json_t *element, int indent) {
|
||||
print_json_indent(indent);
|
||||
printf("JSON Integer: \"%" JSON_INTEGER_FORMAT "\"\n", json_integer_value(element));
|
||||
}
|
||||
|
||||
void print_json_real(json_t *element, int indent) {
|
||||
print_json_indent(indent);
|
||||
printf("JSON Real: %f\n", json_real_value(element));
|
||||
}
|
||||
|
||||
void print_json_true(json_t *element, int indent) {
|
||||
(void)element;
|
||||
print_json_indent(indent);
|
||||
printf("JSON True\n");
|
||||
}
|
||||
|
||||
void print_json_false(json_t *element, int indent) {
|
||||
(void)element;
|
||||
print_json_indent(indent);
|
||||
printf("JSON False\n");
|
||||
}
|
||||
|
||||
void print_json_null(json_t *element, int indent) {
|
||||
(void)element;
|
||||
print_json_indent(indent);
|
||||
printf("JSON Null\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse text into a JSON object. If text is valid JSON, returns a
|
||||
* json_t structure, otherwise prints and error and returns null.
|
||||
*/
|
||||
json_t *load_json(const char *text) {
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
|
||||
root = json_loads(text, 0, &error);
|
||||
|
||||
if (root) {
|
||||
return root;
|
||||
} else {
|
||||
fprintf(stderr, "json error on line %d: %s\n", error.line, error.text);
|
||||
return (json_t *)0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a prompt and return (by reference) a null-terminated line of
|
||||
* text. Returns NULL on eof or some error.
|
||||
*/
|
||||
char *read_line(char *line, int max_chars) {
|
||||
printf("Type some JSON > ");
|
||||
fflush(stdout);
|
||||
return fgets(line, max_chars, stdin);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* main
|
||||
*/
|
||||
|
||||
#define MAX_CHARS 4096
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char line[MAX_CHARS];
|
||||
|
||||
if (argc != 1) {
|
||||
fprintf(stderr, "Usage: %s\n", argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
while (read_line(line, MAX_CHARS) != (char *)NULL) {
|
||||
|
||||
/* parse text into JSON structure */
|
||||
json_t *root = load_json(line);
|
||||
|
||||
if (root) {
|
||||
/* print and release the JSON structure */
|
||||
print_json(root);
|
||||
json_decref(root);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
EXTRA_DIST = jansson.def
|
||||
|
||||
include_HEADERS = jansson.h jansson_config.h
|
||||
include_HEADERS = jansson.h
|
||||
nodist_include_HEADERS = jansson_config.h
|
||||
|
||||
lib_LTLIBRARIES = libjansson.la
|
||||
libjansson_la_SOURCES = \
|
||||
@@ -23,4 +24,4 @@ libjansson_la_SOURCES = \
|
||||
libjansson_la_LDFLAGS = \
|
||||
-no-undefined \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 10:0:6
|
||||
-version-info 14:0:10
|
||||
|
||||
142
src/dump.c
142
src/dump.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,22 +9,30 @@
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "jansson_private.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
struct object_key {
|
||||
size_t serial;
|
||||
const char *key;
|
||||
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
|
||||
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
|
||||
|
||||
struct buffer {
|
||||
const size_t size;
|
||||
size_t used;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||
@@ -32,6 +40,17 @@ static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
|
||||
}
|
||||
|
||||
static int dump_to_buffer(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
struct buffer *buf = (struct buffer *)data;
|
||||
|
||||
if(buf->used + size <= buf->size)
|
||||
memcpy(&buf->data[buf->used], buffer, size);
|
||||
|
||||
buf->used += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
FILE *dest = (FILE *)data;
|
||||
@@ -40,22 +59,36 @@ static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_to_fd(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
int *dest = (int *)data;
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if(write(*dest, buffer, size) == (ssize_t)size)
|
||||
return 0;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 32 spaces (the maximum indentation size) */
|
||||
static const char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
if(FLAGS_TO_INDENT(flags) > 0)
|
||||
{
|
||||
int i, ws_count = JSON_INDENT(flags);
|
||||
unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
|
||||
|
||||
if(dump("\n", 1, data))
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < depth; i++)
|
||||
while(n_spaces > 0)
|
||||
{
|
||||
if(dump(whitespace, ws_count, data))
|
||||
int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
|
||||
|
||||
if(dump(whitespace, cur_n, data))
|
||||
return -1;
|
||||
|
||||
n_spaces -= cur_n;
|
||||
}
|
||||
}
|
||||
else if(space && !(flags & JSON_COMPACT))
|
||||
@@ -65,24 +98,25 @@ static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
|
||||
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
|
||||
{
|
||||
const char *pos, *end;
|
||||
const char *pos, *end, *lim;
|
||||
int32_t codepoint;
|
||||
|
||||
if(dump("\"", 1, data))
|
||||
return -1;
|
||||
|
||||
end = pos = str;
|
||||
lim = str + len;
|
||||
while(1)
|
||||
{
|
||||
const char *text;
|
||||
char seq[13];
|
||||
int length;
|
||||
|
||||
while(*end)
|
||||
while(end < lim)
|
||||
{
|
||||
end = utf8_iterate(pos, &codepoint);
|
||||
end = utf8_iterate(pos, lim - pos, &codepoint);
|
||||
if(!end)
|
||||
return -1;
|
||||
|
||||
@@ -126,7 +160,7 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04x", codepoint);
|
||||
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
@@ -139,7 +173,7 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||
last = 0xDC00 | (codepoint & 0x003ff);
|
||||
|
||||
sprintf(seq, "\\u%04x\\u%04x", first, last);
|
||||
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
|
||||
length = 12;
|
||||
}
|
||||
|
||||
@@ -157,23 +191,18 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
static int object_key_compare_keys(const void *key1, const void *key2)
|
||||
static int compare_keys(const void *key1, const void *key2)
|
||||
{
|
||||
return strcmp(((const struct object_key *)key1)->key,
|
||||
((const struct object_key *)key2)->key);
|
||||
}
|
||||
|
||||
static int object_key_compare_serials(const void *key1, const void *key2)
|
||||
{
|
||||
size_t a = ((const struct object_key *)key1)->serial;
|
||||
size_t b = ((const struct object_key *)key2)->serial;
|
||||
|
||||
return a < b ? -1 : a == b ? 0 : 1;
|
||||
return strcmp(*(const char **)key1, *(const char **)key2);
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
json_dump_callback_t dump, void *data)
|
||||
{
|
||||
int embed = flags & JSON_EMBED;
|
||||
|
||||
flags &= ~JSON_EMBED;
|
||||
|
||||
if(!json)
|
||||
return -1;
|
||||
|
||||
@@ -207,7 +236,8 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
int size;
|
||||
double value = json_real_value(json);
|
||||
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
|
||||
FLAGS_TO_PRECISION(flags));
|
||||
if(size < 0)
|
||||
return -1;
|
||||
|
||||
@@ -215,12 +245,13 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), dump, data, flags);
|
||||
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
size_t n;
|
||||
size_t i;
|
||||
|
||||
json_array_t *array;
|
||||
|
||||
/* detect circular references */
|
||||
@@ -231,11 +262,11 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
if(!embed && dump("[", 1, data))
|
||||
goto array_error;
|
||||
if(n == 0) {
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
return embed ? 0 : dump("]", 1, data);
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
goto array_error;
|
||||
@@ -259,7 +290,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
return embed ? 0 : dump("]", 1, data);
|
||||
|
||||
array_error:
|
||||
array->visited = 0;
|
||||
@@ -290,53 +321,46 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
|
||||
if(dump("{", 1, data))
|
||||
if(!embed && dump("{", 1, data))
|
||||
goto object_error;
|
||||
if(!iter) {
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
return embed ? 0 : dump("}", 1, data);
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
goto object_error;
|
||||
|
||||
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
|
||||
if(flags & JSON_SORT_KEYS)
|
||||
{
|
||||
struct object_key *keys;
|
||||
const char **keys;
|
||||
size_t size, i;
|
||||
int (*cmp_func)(const void *, const void *);
|
||||
|
||||
size = json_object_size(json);
|
||||
keys = jsonp_malloc(size * sizeof(struct object_key));
|
||||
keys = jsonp_malloc(size * sizeof(const char *));
|
||||
if(!keys)
|
||||
goto object_error;
|
||||
|
||||
i = 0;
|
||||
while(iter)
|
||||
{
|
||||
keys[i].serial = hashtable_iter_serial(iter);
|
||||
keys[i].key = json_object_iter_key(iter);
|
||||
keys[i] = json_object_iter_key(iter);
|
||||
iter = json_object_iter_next((json_t *)json, iter);
|
||||
i++;
|
||||
}
|
||||
assert(i == size);
|
||||
|
||||
if(flags & JSON_SORT_KEYS)
|
||||
cmp_func = object_key_compare_keys;
|
||||
else
|
||||
cmp_func = object_key_compare_serials;
|
||||
|
||||
qsort(keys, size, sizeof(struct object_key), cmp_func);
|
||||
qsort(keys, size, sizeof(const char *), compare_keys);
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = keys[i].key;
|
||||
key = keys[i];
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, dump, data, flags);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
@@ -372,8 +396,9 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
const char *key = json_object_iter_key(iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), dump, data, flags);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
@@ -396,7 +421,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
return embed ? 0 : dump("}", 1, data);
|
||||
|
||||
object_error:
|
||||
object->visited = 0;
|
||||
@@ -426,11 +451,26 @@ char *json_dumps(const json_t *json, size_t flags)
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
|
||||
{
|
||||
struct buffer buf = { size, 0, buffer };
|
||||
|
||||
if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
|
||||
return 0;
|
||||
|
||||
return buf.used;
|
||||
}
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
{
|
||||
return json_dump_callback(json, dump_to_file, (void *)output, flags);
|
||||
}
|
||||
|
||||
int json_dumpfd(const json_t *json, int output, size_t flags)
|
||||
{
|
||||
return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
|
||||
}
|
||||
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
{
|
||||
int result;
|
||||
|
||||
@@ -25,11 +25,11 @@ void jsonp_error_set_source(json_error_t *error, const char *source)
|
||||
|
||||
length = strlen(source);
|
||||
if(length < JSON_ERROR_SOURCE_LENGTH)
|
||||
strcpy(error->source, source);
|
||||
strncpy(error->source, source, length + 1);
|
||||
else {
|
||||
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
|
||||
strcpy(error->source, "...");
|
||||
strcpy(error->source + 3, source + extra);
|
||||
strncpy(error->source, "...", 3);
|
||||
strncpy(error->source + 3, source + extra, length - extra + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
|
||||
error->line = line;
|
||||
error->column = column;
|
||||
error->position = position;
|
||||
error->position = (int)position;
|
||||
|
||||
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "jansson_private.h" /* for container_of() */
|
||||
#include "hashtable.h"
|
||||
|
||||
#ifndef INITIAL_HASHTABLE_ORDER
|
||||
#define INITIAL_HASHTABLE_ORDER 3
|
||||
#endif
|
||||
|
||||
typedef struct hashtable_list list_t;
|
||||
typedef struct hashtable_pair pair_t;
|
||||
typedef struct hashtable_bucket bucket_t;
|
||||
@@ -30,6 +34,7 @@ extern volatile uint32_t hashtable_seed;
|
||||
#include "lookup3.h"
|
||||
|
||||
#define list_to_pair(list_) container_of(list_, pair_t, list)
|
||||
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
|
||||
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
|
||||
|
||||
static JSON_INLINE void list_init(list_t *list)
|
||||
@@ -122,6 +127,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
bucket->last = pair->list.prev;
|
||||
|
||||
list_remove(&pair->list);
|
||||
list_remove(&pair->ordered_list);
|
||||
json_decref(pair->value);
|
||||
|
||||
jsonp_free(pair);
|
||||
@@ -148,16 +154,19 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
size_t i, index, new_size;
|
||||
size_t i, index, new_size, new_order;
|
||||
struct hashtable_bucket *new_buckets;
|
||||
|
||||
new_order = hashtable->order + 1;
|
||||
new_size = hashsize(new_order);
|
||||
|
||||
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!new_buckets)
|
||||
return -1;
|
||||
|
||||
jsonp_free(hashtable->buckets);
|
||||
|
||||
hashtable->order++;
|
||||
new_size = hashsize(hashtable->order);
|
||||
|
||||
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
hashtable->buckets = new_buckets;
|
||||
hashtable->order = new_order;
|
||||
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
@@ -184,12 +193,13 @@ int hashtable_init(hashtable_t *hashtable)
|
||||
size_t i;
|
||||
|
||||
hashtable->size = 0;
|
||||
hashtable->order = 3;
|
||||
hashtable->order = INITIAL_HASHTABLE_ORDER;
|
||||
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
list_init(&hashtable->list);
|
||||
list_init(&hashtable->ordered_list);
|
||||
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
@@ -206,9 +216,7 @@ void hashtable_close(hashtable_t *hashtable)
|
||||
jsonp_free(hashtable->buckets);
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value)
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
|
||||
{
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
@@ -234,17 +242,25 @@ int hashtable_set(hashtable_t *hashtable,
|
||||
/* offsetof(...) returns the size of pair_t without the last,
|
||||
flexible member. This way, the correct amount is
|
||||
allocated. */
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1);
|
||||
|
||||
size_t len = strlen(key);
|
||||
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
|
||||
/* Avoid an overflow if the key is very long */
|
||||
return -1;
|
||||
}
|
||||
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
|
||||
if(!pair)
|
||||
return -1;
|
||||
|
||||
pair->hash = hash;
|
||||
pair->serial = serial;
|
||||
strcpy(pair->key, key);
|
||||
strncpy(pair->key, key, len + 1);
|
||||
pair->value = value;
|
||||
list_init(&pair->list);
|
||||
list_init(&pair->ordered_list);
|
||||
|
||||
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||
list_insert(&hashtable->ordered_list, &pair->ordered_list);
|
||||
|
||||
hashtable->size++;
|
||||
}
|
||||
@@ -286,12 +302,13 @@ void hashtable_clear(hashtable_t *hashtable)
|
||||
}
|
||||
|
||||
list_init(&hashtable->list);
|
||||
list_init(&hashtable->ordered_list);
|
||||
hashtable->size = 0;
|
||||
}
|
||||
|
||||
void *hashtable_iter(hashtable_t *hashtable)
|
||||
{
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
@@ -307,38 +324,32 @@ void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
if(!pair)
|
||||
return NULL;
|
||||
|
||||
return &pair->list;
|
||||
return &pair->ordered_list;
|
||||
}
|
||||
|
||||
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
|
||||
{
|
||||
list_t *list = (list_t *)iter;
|
||||
if(list->next == &hashtable->list)
|
||||
if(list->next == &hashtable->ordered_list)
|
||||
return NULL;
|
||||
return list->next;
|
||||
}
|
||||
|
||||
void *hashtable_iter_key(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->key;
|
||||
}
|
||||
|
||||
size_t hashtable_iter_serial(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
return pair->serial;
|
||||
}
|
||||
|
||||
void *hashtable_iter_value(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void hashtable_iter_set(void *iter, json_t *value)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
|
||||
json_decref(pair->value);
|
||||
pair->value = value;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifndef HASHTABLE_H
|
||||
#define HASHTABLE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "jansson.h"
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
@@ -17,10 +20,10 @@ struct hashtable_list {
|
||||
key-value pair. In this case, it just encodes some extra data,
|
||||
too */
|
||||
struct hashtable_pair {
|
||||
size_t hash;
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
size_t hash;
|
||||
json_t *value;
|
||||
size_t serial;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
@@ -34,11 +37,12 @@ typedef struct hashtable {
|
||||
struct hashtable_bucket *buckets;
|
||||
size_t order; /* hashtable has pow(2, order) buckets */
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
} hashtable_t;
|
||||
|
||||
|
||||
#define hashtable_key_to_iter(key_) \
|
||||
(&(container_of(key_, struct hashtable_pair, key)->list))
|
||||
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
|
||||
|
||||
|
||||
/**
|
||||
@@ -77,9 +81,7 @@ void hashtable_close(hashtable_t *hashtable);
|
||||
*
|
||||
* Returns 0 on success, -1 on failure (out of memory).
|
||||
*/
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value);
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
|
||||
|
||||
/**
|
||||
* hashtable_get - Get a value associated with a key
|
||||
@@ -156,13 +158,6 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
|
||||
*/
|
||||
void *hashtable_iter_key(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_serial - Retrieve the serial number pointed to by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
*/
|
||||
size_t hashtable_iter_serial(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_value - Retrieve the value pointed by an iterator
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -38,8 +38,8 @@
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* For _getpid() */
|
||||
#include <process.h>
|
||||
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
@@ -95,7 +95,6 @@ static int seed_from_urandom(uint32_t *seed) {
|
||||
|
||||
/* 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);
|
||||
@@ -112,7 +111,7 @@ static int seed_from_windows_cryptoapi(uint32_t *seed)
|
||||
BYTE data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
hAdvAPI32 = GetModuleHandle("advapi32.dll");
|
||||
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
|
||||
if(hAdvAPI32 == NULL)
|
||||
return 1;
|
||||
|
||||
@@ -131,7 +130,7 @@ static int seed_from_windows_cryptoapi(uint32_t *seed)
|
||||
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||
return 1;
|
||||
|
||||
ok = CryptGenRandom(hCryptProv, sizeof(uint32_t), data);
|
||||
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
|
||||
pCryptReleaseContext(hCryptProv, 0);
|
||||
|
||||
if (!ok)
|
||||
@@ -156,7 +155,7 @@ static int seed_from_timestamp_and_pid(uint32_t *seed) {
|
||||
|
||||
/* XOR with PID for more randomness */
|
||||
#if defined(_WIN32)
|
||||
*seed ^= (uint32_t)_getpid();
|
||||
*seed ^= (uint32_t)GetCurrentProcessId();
|
||||
#elif defined(HAVE_GETPID)
|
||||
*seed ^= (uint32_t)getpid();
|
||||
#endif
|
||||
@@ -169,12 +168,12 @@ static uint32_t generate_seed() {
|
||||
int done = 0;
|
||||
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
if (!done && seed_from_urandom(&seed) == 0)
|
||||
if (seed_from_urandom(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
if (!done && seed_from_windows_cryptoapi(&seed) == 0)
|
||||
if (seed_from_windows_cryptoapi(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,10 +4,15 @@ EXPORTS
|
||||
json_false
|
||||
json_null
|
||||
json_string
|
||||
json_stringn
|
||||
json_string_nocheck
|
||||
json_stringn_nocheck
|
||||
json_string_value
|
||||
json_string_length
|
||||
json_string_set
|
||||
json_string_setn
|
||||
json_string_set_nocheck
|
||||
json_string_setn_nocheck
|
||||
json_integer
|
||||
json_integer_value
|
||||
json_integer_set
|
||||
@@ -43,12 +48,15 @@ EXPORTS
|
||||
json_object_key_to_iter
|
||||
json_object_seed
|
||||
json_dumps
|
||||
json_dumpb
|
||||
json_dumpf
|
||||
json_dumpfd
|
||||
json_dump_file
|
||||
json_dump_callback
|
||||
json_loads
|
||||
json_loadb
|
||||
json_loadf
|
||||
json_loadfd
|
||||
json_load_file
|
||||
json_load_callback
|
||||
json_equal
|
||||
@@ -61,4 +69,5 @@ EXPORTS
|
||||
json_unpack_ex
|
||||
json_vunpack_ex
|
||||
json_set_alloc_funcs
|
||||
json_get_alloc_funcs
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <stdlib.h> /* for size_t */
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <jansson_config.h>
|
||||
#include "jansson_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -21,11 +21,11 @@ extern "C" {
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 6
|
||||
#define JANSSON_MINOR_VERSION 10
|
||||
#define JANSSON_MICRO_VERSION 0
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.6"
|
||||
#define JANSSON_VERSION "2.10"
|
||||
|
||||
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
|
||||
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
|
||||
@@ -67,23 +67,26 @@ typedef long json_int_t;
|
||||
#endif
|
||||
|
||||
#define json_typeof(json) ((json)->type)
|
||||
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY)
|
||||
#define json_is_string(json) (json && json_typeof(json) == JSON_STRING)
|
||||
#define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER)
|
||||
#define json_is_real(json) (json && json_typeof(json) == JSON_REAL)
|
||||
#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
|
||||
#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
|
||||
#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
|
||||
#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
|
||||
#define json_is_number(json) (json_is_integer(json) || json_is_real(json))
|
||||
#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE)
|
||||
#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE)
|
||||
#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
|
||||
#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
|
||||
#define json_boolean_value json_is_true
|
||||
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
|
||||
#define json_is_null(json) (json && json_typeof(json) == JSON_NULL)
|
||||
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
|
||||
|
||||
/* construction, destruction, reference counting */
|
||||
|
||||
json_t *json_object(void);
|
||||
json_t *json_array(void);
|
||||
json_t *json_string(const char *value);
|
||||
json_t *json_stringn(const char *value, size_t len);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len);
|
||||
json_t *json_integer(json_int_t value);
|
||||
json_t *json_real(double value);
|
||||
json_t *json_true(void);
|
||||
@@ -109,6 +112,19 @@ void json_decref(json_t *json)
|
||||
json_delete(json);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
static JSON_INLINE
|
||||
void json_decrefp(json_t **json)
|
||||
{
|
||||
if(json) {
|
||||
json_decref(*json);
|
||||
*json = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define json_auto_t json_t __attribute__((cleanup(json_decrefp)))
|
||||
#endif
|
||||
|
||||
|
||||
/* error reporting */
|
||||
|
||||
@@ -149,6 +165,13 @@ int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
|
||||
key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
|
||||
|
||||
#define json_object_foreach_safe(object, n, key, value) \
|
||||
for(key = json_object_iter_key(json_object_iter(object)), \
|
||||
n = json_object_iter_next(object, json_object_key_to_iter(key)); \
|
||||
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
|
||||
key = json_object_iter_key(n), \
|
||||
n = json_object_iter_next(object, json_object_key_to_iter(key)))
|
||||
|
||||
#define json_array_foreach(array, index, value) \
|
||||
for(index = 0; \
|
||||
index < json_array_size(array) && (value = json_array_get(array, index)); \
|
||||
@@ -200,16 +223,18 @@ int json_array_insert(json_t *array, size_t ind, json_t *value)
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
size_t json_string_length(const json_t *string);
|
||||
json_int_t json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(json_t *string, const char *value);
|
||||
int json_string_setn(json_t *string, const char *value, size_t len);
|
||||
int json_string_set_nocheck(json_t *string, const char *value);
|
||||
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
|
||||
int json_integer_set(json_t *integer, json_int_t value);
|
||||
int json_real_set(json_t *real, double value);
|
||||
|
||||
|
||||
/* pack, unpack */
|
||||
|
||||
json_t *json_pack(const char *fmt, ...);
|
||||
@@ -241,30 +266,37 @@ json_t *json_deep_copy(const json_t *value);
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
#define JSON_DECODE_ANY 0x4
|
||||
#define JSON_DECODE_INT_AS_REAL 0x8
|
||||
#define JSON_ALLOW_NUL 0x10
|
||||
|
||||
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
|
||||
|
||||
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadfd(int input, size_t flags, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
|
||||
json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
#define JSON_ESCAPE_SLASH 0x400
|
||||
#define JSON_MAX_INDENT 0x1F
|
||||
#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
#define JSON_ESCAPE_SLASH 0x400
|
||||
#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11)
|
||||
#define JSON_EMBED 0x10000
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
char *json_dumps(const json_t *json, size_t flags);
|
||||
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags);
|
||||
int json_dumpfd(const json_t *json, int output, size_t flags);
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags);
|
||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
|
||||
|
||||
@@ -274,6 +306,7 @@ typedef void *(*json_malloc_t)(size_t);
|
||||
typedef void (*json_free_t)(void *);
|
||||
|
||||
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
|
||||
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -36,4 +36,8 @@
|
||||
otherwise to 0. */
|
||||
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
|
||||
|
||||
/* Maximum recursion depth for parsing JSON input.
|
||||
This limits the depth of e.g. array-within-array constructions. */
|
||||
#define JSON_PARSER_MAX_DEPTH 2048
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef JANSSON_PRIVATE_H
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
#include "jansson_private_config.h"
|
||||
#include <stddef.h>
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
@@ -34,7 +35,6 @@
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
size_t serial;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
@@ -49,6 +49,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
size_t length;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -64,9 +65,13 @@ typedef struct {
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
/* Create a string by taking ownership of an existing buffer */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
|
||||
|
||||
/* Error message formatting */
|
||||
void jsonp_error_init(json_error_t *error, const char *source);
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source);
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
@@ -76,18 +81,29 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
|
||||
/* Locale independent string<->double conversions */
|
||||
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value);
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
|
||||
|
||||
/* Wrappers for custom memory functions */
|
||||
void* jsonp_malloc(size_t size);
|
||||
void jsonp_free(void *ptr);
|
||||
char *jsonp_strndup(const char *str, size_t length);
|
||||
char *jsonp_strdup(const char *str);
|
||||
char *jsonp_strndup(const char *str, size_t len);
|
||||
|
||||
|
||||
/* Windows compatibility */
|
||||
#ifdef _WIN32
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
# if defined(_MSC_VER) /* MS compiller */
|
||||
# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */
|
||||
# define snprintf _snprintf
|
||||
# endif
|
||||
# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */
|
||||
# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a)
|
||||
# endif
|
||||
# else /* Other Windows compiller, old definition */
|
||||
# define snprintf _snprintf
|
||||
# define vsnprintf _vsnprintf
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
218
src/load.c
218
src/load.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,15 +9,19 @@
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "jansson_private.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
@@ -61,9 +65,14 @@ typedef struct {
|
||||
typedef struct {
|
||||
stream_t stream;
|
||||
strbuffer_t saved_text;
|
||||
size_t flags;
|
||||
size_t depth;
|
||||
int token;
|
||||
union {
|
||||
char *string;
|
||||
struct {
|
||||
char *val;
|
||||
size_t len;
|
||||
} string;
|
||||
json_int_t integer;
|
||||
double real;
|
||||
} value;
|
||||
@@ -166,7 +175,7 @@ static int stream_get(stream_t *stream, json_error_t *error)
|
||||
if(0x80 <= c && c <= 0xFF)
|
||||
{
|
||||
/* multi-byte UTF-8 sequence */
|
||||
int i, count;
|
||||
size_t i, count;
|
||||
|
||||
count = utf8_check_first(c);
|
||||
if(!count)
|
||||
@@ -262,7 +271,7 @@ static void lex_unget_unsave(lex_t *lex, int c)
|
||||
#endif
|
||||
stream_unget(&lex->stream, c);
|
||||
#ifndef NDEBUG
|
||||
d =
|
||||
d =
|
||||
#endif
|
||||
strbuffer_pop(&lex->saved_text);
|
||||
assert(c == d);
|
||||
@@ -279,6 +288,13 @@ static void lex_save_cached(lex_t *lex)
|
||||
}
|
||||
}
|
||||
|
||||
static void lex_free_string(lex_t *lex)
|
||||
{
|
||||
jsonp_free(lex->value.string.val);
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
|
||||
/* assumes that str points to 'u' plus at least 4 valid hex digits */
|
||||
static int32_t decode_unicode_escape(const char *str)
|
||||
{
|
||||
@@ -297,7 +313,7 @@ static int32_t decode_unicode_escape(const char *str)
|
||||
else if(l_isupper(c))
|
||||
value += c - 'A' + 10;
|
||||
else
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -310,7 +326,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
char *t;
|
||||
int i;
|
||||
|
||||
lex->value.string = NULL;
|
||||
lex->value.string.val = NULL;
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
@@ -328,7 +344,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
/* control character */
|
||||
lex_unget_unsave(lex, c);
|
||||
if(c == '\n')
|
||||
error_set(error, lex, "unexpected newline", c);
|
||||
error_set(error, lex, "unexpected newline");
|
||||
else
|
||||
error_set(error, lex, "control character 0x%x", c);
|
||||
goto out;
|
||||
@@ -365,14 +381,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
|
||||
are converted to 4 bytes
|
||||
*/
|
||||
lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!lex->value.string) {
|
||||
t = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!t) {
|
||||
/* this is not very nice, since TOKEN_INVALID is returned */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the target */
|
||||
t = lex->value.string;
|
||||
lex->value.string.val = t;
|
||||
|
||||
/* + 1 to skip the " */
|
||||
p = strbuffer_value(&lex->saved_text) + 1;
|
||||
@@ -381,17 +395,24 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(*p == '\\') {
|
||||
p++;
|
||||
if(*p == 'u') {
|
||||
char buffer[4];
|
||||
int length;
|
||||
size_t length;
|
||||
int32_t value;
|
||||
|
||||
value = decode_unicode_escape(p);
|
||||
if(value < 0) {
|
||||
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xD800 <= value && value <= 0xDBFF) {
|
||||
/* surrogate pair */
|
||||
if(*p == '\\' && *(p + 1) == 'u') {
|
||||
int32_t value2 = decode_unicode_escape(++p);
|
||||
if(value2 < 0) {
|
||||
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
@@ -420,16 +441,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
error_set(error, lex, "invalid Unicode '\\u%04X'", value);
|
||||
goto out;
|
||||
}
|
||||
else if(value == 0)
|
||||
{
|
||||
error_set(error, lex, "\\u0000 is not allowed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(utf8_encode(value, buffer, &length))
|
||||
if(utf8_encode(value, t, &length))
|
||||
assert(0);
|
||||
|
||||
memcpy(t, buffer, length);
|
||||
t += length;
|
||||
}
|
||||
else {
|
||||
@@ -451,11 +465,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
*(t++) = *(p++);
|
||||
}
|
||||
*t = '\0';
|
||||
lex->value.string.len = t - lex->value.string.val;
|
||||
lex->token = TOKEN_STRING;
|
||||
return;
|
||||
|
||||
out:
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
}
|
||||
|
||||
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
|
||||
@@ -474,7 +489,7 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
{
|
||||
const char *saved_text;
|
||||
char *end;
|
||||
double value;
|
||||
double doubleval;
|
||||
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
@@ -489,26 +504,28 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
}
|
||||
}
|
||||
else if(l_isdigit(c)) {
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c));
|
||||
}
|
||||
else {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(c != '.' && c != 'E' && c != 'e') {
|
||||
json_int_t value;
|
||||
if(!(lex->flags & JSON_DECODE_INT_AS_REAL) &&
|
||||
c != '.' && c != 'E' && c != 'e')
|
||||
{
|
||||
json_int_t intval;
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
|
||||
errno = 0;
|
||||
value = json_strtoint(saved_text, &end, 10);
|
||||
intval = json_strtoint(saved_text, &end, 10);
|
||||
if(errno == ERANGE) {
|
||||
if(value < 0)
|
||||
if(intval < 0)
|
||||
error_set(error, lex, "too big negative integer");
|
||||
else
|
||||
error_set(error, lex, "too big integer");
|
||||
@@ -518,7 +535,7 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
lex->token = TOKEN_INTEGER;
|
||||
lex->value.integer = value;
|
||||
lex->value.integer = intval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -530,9 +547,9 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
}
|
||||
lex_save(lex, c);
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c));
|
||||
}
|
||||
|
||||
if(c == 'E' || c == 'e') {
|
||||
@@ -545,20 +562,20 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isdigit(c));
|
||||
}
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
if(jsonp_strtod(&lex->saved_text, &value)) {
|
||||
if(jsonp_strtod(&lex->saved_text, &doubleval)) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lex->token = TOKEN_REAL;
|
||||
lex->value.real = value;
|
||||
lex->value.real = doubleval;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -571,14 +588,12 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
jsonp_free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
if(lex->token == TOKEN_STRING)
|
||||
lex_free_string(lex);
|
||||
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
do
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r');
|
||||
|
||||
if(c == STREAM_STATE_EOF) {
|
||||
lex->token = TOKEN_EOF;
|
||||
@@ -607,9 +622,9 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
/* eat up the whole identifier for clearer error messages */
|
||||
const char *saved_text;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isalpha(c))
|
||||
do
|
||||
c = lex_get_save(lex, error);
|
||||
while(l_isalpha(c));
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
@@ -635,23 +650,25 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
static char *lex_steal_string(lex_t *lex, size_t *out_len)
|
||||
{
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
result = lex->value.string.val;
|
||||
*out_len = lex->value.string.len;
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
static int lex_init(lex_t *lex, get_func get, size_t flags, void *data)
|
||||
{
|
||||
stream_init(&lex->stream, get, data);
|
||||
if(strbuffer_init(&lex->saved_text))
|
||||
return -1;
|
||||
|
||||
lex->flags = flags;
|
||||
lex->token = TOKEN_INVALID;
|
||||
return 0;
|
||||
}
|
||||
@@ -659,7 +676,7 @@ static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
static void lex_close(lex_t *lex)
|
||||
{
|
||||
if(lex->token == TOKEN_STRING)
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
strbuffer_close(&lex->saved_text);
|
||||
}
|
||||
|
||||
@@ -680,6 +697,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
while(1) {
|
||||
char *key;
|
||||
size_t len;
|
||||
json_t *value;
|
||||
|
||||
if(lex->token != TOKEN_STRING) {
|
||||
@@ -687,9 +705,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = lex_steal_string(lex);
|
||||
key = lex_steal_string(lex, &len);
|
||||
if(!key)
|
||||
return NULL;
|
||||
if (memchr(key, '\0', len)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "NUL byte in object key not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
@@ -784,24 +807,35 @@ error:
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
double value;
|
||||
|
||||
lex->depth++;
|
||||
if(lex->depth > JSON_PARSER_MAX_DEPTH) {
|
||||
error_set(error, lex, "maximum parsing depth reached");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(lex->token) {
|
||||
case TOKEN_STRING: {
|
||||
json = json_string_nocheck(lex->value.string);
|
||||
const char *value = lex->value.string.val;
|
||||
size_t len = lex->value.string.len;
|
||||
|
||||
if(!(flags & JSON_ALLOW_NUL)) {
|
||||
if(memchr(value, '\0', len)) {
|
||||
error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
json = jsonp_stringn_nocheck_own(value, len);
|
||||
if(json) {
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TOKEN_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);
|
||||
}
|
||||
json = json_integer(lex->value.integer);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -842,6 +876,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
lex->depth--;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -849,6 +884,8 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
lex->depth = 0;
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(!(flags & JSON_DECODE_ANY)) {
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
@@ -872,7 +909,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
if(error) {
|
||||
/* Save the position even though there was no error */
|
||||
error->position = lex->stream.position;
|
||||
error->position = (int)lex->stream.position;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -881,7 +918,7 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
typedef struct
|
||||
{
|
||||
const char *data;
|
||||
int pos;
|
||||
size_t pos;
|
||||
} string_data_t;
|
||||
|
||||
static int string_get(void *data)
|
||||
@@ -914,7 +951,7 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
|
||||
if(lex_init(&lex, string_get, (void *)&stream_data))
|
||||
if(lex_init(&lex, string_get, flags, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
@@ -959,7 +996,7 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
||||
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
||||
if(lex_init(&lex, buffer_get, flags, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
@@ -986,7 +1023,46 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
if(lex_init(&lex, (get_func)fgetc, flags, input))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int fd_get_func(int *fd)
|
||||
{
|
||||
uint8_t c;
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if (read(*fd, &c, 1) == 1)
|
||||
return c;
|
||||
#endif
|
||||
return EOF;
|
||||
}
|
||||
|
||||
json_t *json_loadfd(int input, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if(input == STDIN_FILENO)
|
||||
source = "<stdin>";
|
||||
else
|
||||
#endif
|
||||
source = "<stream>";
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
|
||||
if (input < 0) {
|
||||
error_set(error, NULL, "wrong arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)fd_get_func, flags, &input))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
@@ -1067,7 +1143,7 @@ json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flag
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(lex_init(&lex, (get_func)callback_get, &stream_data))
|
||||
if(lex_init(&lex, (get_func)callback_get, flags, &stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
@@ -37,7 +37,7 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
@@ -205,7 +205,22 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
|
||||
/* Detect Valgrind or AddressSanitizer */
|
||||
#ifdef VALGRIND
|
||||
# define NO_MASKING_TRICK 1
|
||||
#else
|
||||
# if defined(__has_feature) /* Clang */
|
||||
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# else
|
||||
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NO_MASKING_TRICK
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
@@ -230,7 +245,7 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
#ifndef NO_MASKING_TRICK
|
||||
|
||||
switch(length)
|
||||
{
|
||||
|
||||
27
src/memory.c
27
src/memory.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify it
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
|
||||
/* C89 allows these to be macros */
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
/* memory function pointers */
|
||||
static json_malloc_t do_malloc = malloc;
|
||||
static json_free_t do_free = free;
|
||||
@@ -34,18 +38,19 @@ void jsonp_free(void *ptr)
|
||||
|
||||
char *jsonp_strdup(const char *str)
|
||||
{
|
||||
char *new_str;
|
||||
size_t len;
|
||||
return jsonp_strndup(str, strlen(str));
|
||||
}
|
||||
|
||||
len = strlen(str);
|
||||
if(len == (size_t)-1)
|
||||
return NULL;
|
||||
char *jsonp_strndup(const char *str, size_t len)
|
||||
{
|
||||
char *new_str;
|
||||
|
||||
new_str = jsonp_malloc(len + 1);
|
||||
if(!new_str)
|
||||
return NULL;
|
||||
|
||||
memcpy(new_str, str, len + 1);
|
||||
memcpy(new_str, str, len);
|
||||
new_str[len] = '\0';
|
||||
return new_str;
|
||||
}
|
||||
|
||||
@@ -54,3 +59,11 @@ void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
|
||||
do_malloc = malloc_fn;
|
||||
do_free = free_fn;
|
||||
}
|
||||
|
||||
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
|
||||
{
|
||||
if (malloc_fn)
|
||||
*malloc_fn = do_malloc;
|
||||
if (free_fn)
|
||||
*free_fn = do_free;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
@@ -48,7 +48,6 @@ static const char * const type_names[] = {
|
||||
|
||||
static const char unpack_value_starters[] = "{[siIbfFOon";
|
||||
|
||||
|
||||
static void scanner_init(scanner_t *s, json_error_t *error,
|
||||
size_t flags, const char *fmt)
|
||||
{
|
||||
@@ -125,19 +124,18 @@ 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)
|
||||
const char *purpose, size_t *out_len, 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 != '+') {
|
||||
if(t != '#' && t != '%' && t != '+') {
|
||||
/* Optimize the simple case */
|
||||
str = va_arg(*ap, const char *);
|
||||
|
||||
@@ -146,11 +144,14 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!utf8_check_string(str, -1)) {
|
||||
length = strlen(str);
|
||||
|
||||
if(!utf8_check_string(str, length)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = length;
|
||||
*ours = 0;
|
||||
return (char *)str;
|
||||
}
|
||||
@@ -170,6 +171,9 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
if(token(s) == '#') {
|
||||
length = va_arg(*ap, int);
|
||||
}
|
||||
else if(token(s) == '%') {
|
||||
length = va_arg(*ap, size_t);
|
||||
}
|
||||
else {
|
||||
prev_token(s);
|
||||
length = strlen(str);
|
||||
@@ -188,15 +192,15 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
}
|
||||
}
|
||||
|
||||
result = strbuffer_steal_value(&strbuff);
|
||||
|
||||
if(!utf8_check_string(result, -1)) {
|
||||
if(!utf8_check_string(strbuff.value, strbuff.length)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = strbuff.length;
|
||||
*ours = 1;
|
||||
return result;
|
||||
return strbuffer_steal_value(&strbuff);
|
||||
}
|
||||
|
||||
static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
@@ -206,6 +210,7 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
|
||||
while(token(s) != '}') {
|
||||
char *key;
|
||||
size_t len;
|
||||
int ours;
|
||||
json_t *value;
|
||||
|
||||
@@ -219,21 +224,25 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = read_string(s, ap, "object key", &ours);
|
||||
key = read_string(s, ap, "object key", &len, &ours);
|
||||
if(!key)
|
||||
goto error;
|
||||
|
||||
next_token(s);
|
||||
|
||||
value = pack(s, ap);
|
||||
if(!value)
|
||||
goto error;
|
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
if(!value) {
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -281,6 +290,28 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *pack_string(scanner_t *s, va_list *ap)
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int ours;
|
||||
int nullable;
|
||||
|
||||
next_token(s);
|
||||
nullable = token(s) == '?';
|
||||
if (!nullable)
|
||||
prev_token(s);
|
||||
|
||||
str = read_string(s, ap, "string", &len, &ours);
|
||||
if (!str) {
|
||||
return nullable ? json_null() : NULL;
|
||||
} else if (ours) {
|
||||
return jsonp_stringn_nocheck_own(str, len);
|
||||
} else {
|
||||
return json_stringn_nocheck(str, len);
|
||||
}
|
||||
}
|
||||
|
||||
static json_t *pack(scanner_t *s, va_list *ap)
|
||||
{
|
||||
switch(token(s)) {
|
||||
@@ -290,21 +321,8 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
case '[':
|
||||
return pack_array(s, ap);
|
||||
|
||||
case 's': { /* string */
|
||||
char *str;
|
||||
int ours;
|
||||
json_t *result;
|
||||
|
||||
str = read_string(s, ap, "string", &ours);
|
||||
if(!str)
|
||||
return NULL;
|
||||
|
||||
result = json_string_nocheck(str);
|
||||
if(ours)
|
||||
jsonp_free(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
case 's': /* string */
|
||||
return pack_string(s, ap);
|
||||
|
||||
case 'n': /* null */
|
||||
return json_null();
|
||||
@@ -322,10 +340,40 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
return json_real(va_arg(*ap, double));
|
||||
|
||||
case 'O': /* a json_t object; increments refcount */
|
||||
return json_incref(va_arg(*ap, json_t *));
|
||||
{
|
||||
int nullable;
|
||||
json_t *json;
|
||||
|
||||
next_token(s);
|
||||
nullable = token(s) == '?';
|
||||
if (!nullable)
|
||||
prev_token(s);
|
||||
|
||||
json = va_arg(*ap, json_t *);
|
||||
if (!json && nullable) {
|
||||
return json_null();
|
||||
} else {
|
||||
return json_incref(json);
|
||||
}
|
||||
}
|
||||
|
||||
case 'o': /* a json_t object; doesn't increment refcount */
|
||||
return va_arg(*ap, json_t *);
|
||||
{
|
||||
int nullable;
|
||||
json_t *json;
|
||||
|
||||
next_token(s);
|
||||
nullable = token(s) == '?';
|
||||
if (!nullable)
|
||||
prev_token(s);
|
||||
|
||||
json = va_arg(*ap, json_t *);
|
||||
if (!json && nullable) {
|
||||
return json_null();
|
||||
} else {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||
@@ -340,6 +388,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
{
|
||||
int ret = -1;
|
||||
int strict = 0;
|
||||
int gotopt = 0;
|
||||
|
||||
/* Use a set (emulated by a hashtable) to check that all object
|
||||
keys are accessed. Checking that the correct number of keys
|
||||
@@ -396,7 +445,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '?') {
|
||||
opt = 1;
|
||||
opt = gotopt = 1;
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
@@ -415,17 +464,61 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
if(unpack(s, value, ap))
|
||||
goto out;
|
||||
|
||||
hashtable_set(&key_set, key, 0, json_null());
|
||||
hashtable_set(&key_set, key, json_null());
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||
strict = 1;
|
||||
|
||||
if(root && strict == 1 && key_set.size != json_object_size(root)) {
|
||||
long diff = (long)json_object_size(root) - (long)key_set.size;
|
||||
set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
|
||||
goto out;
|
||||
if(root && strict == 1) {
|
||||
/* We need to check that all non optional items have been parsed */
|
||||
const char *key;
|
||||
int have_unrecognized_keys = 0;
|
||||
strbuffer_t unrecognized_keys;
|
||||
json_t *value;
|
||||
long unpacked = 0;
|
||||
if (gotopt) {
|
||||
/* We have optional keys, we need to iter on each key */
|
||||
json_object_foreach(root, key, value) {
|
||||
if(!hashtable_get(&key_set, key)) {
|
||||
unpacked++;
|
||||
|
||||
/* Save unrecognized keys for the error message */
|
||||
if (!have_unrecognized_keys) {
|
||||
strbuffer_init(&unrecognized_keys);
|
||||
have_unrecognized_keys = 1;
|
||||
} else {
|
||||
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
|
||||
}
|
||||
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* No optional keys, we can just compare the number of items */
|
||||
unpacked = (long)json_object_size(root) - (long)key_set.size;
|
||||
}
|
||||
if (unpacked) {
|
||||
if (!gotopt) {
|
||||
/* Save unrecognized keys for the error message */
|
||||
json_object_foreach(root, key, value) {
|
||||
if(!hashtable_get(&key_set, key)) {
|
||||
if (!have_unrecognized_keys) {
|
||||
strbuffer_init(&unrecognized_keys);
|
||||
have_unrecognized_keys = 1;
|
||||
} else {
|
||||
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
|
||||
}
|
||||
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
set_error(s, "<validation>",
|
||||
"%li object item(s) left unpacked: %s",
|
||||
unpacked, strbuffer_value(&unrecognized_keys));
|
||||
strbuffer_close(&unrecognized_keys);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@@ -523,16 +616,32 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
const char **target;
|
||||
const char **str_target;
|
||||
size_t *len_target = NULL;
|
||||
|
||||
target = va_arg(*ap, const char **);
|
||||
if(!target) {
|
||||
str_target = va_arg(*ap, const char **);
|
||||
if(!str_target) {
|
||||
set_error(s, "<args>", "NULL string argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(root)
|
||||
*target = json_string_value(root);
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '%') {
|
||||
len_target = va_arg(*ap, size_t *);
|
||||
if(!len_target) {
|
||||
set_error(s, "<args>", "NULL string length argument");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
prev_token(s);
|
||||
|
||||
if(root) {
|
||||
*str_target = json_string_value(root);
|
||||
if(len_target)
|
||||
*len_target = json_string_length(root);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -60,11 +60,6 @@ char *strbuffer_steal_value(strbuffer_t *strbuff)
|
||||
return result;
|
||||
}
|
||||
|
||||
int strbuffer_append(strbuffer_t *strbuff, const char *string)
|
||||
{
|
||||
return strbuffer_append_bytes(strbuff, string, strlen(string));
|
||||
}
|
||||
|
||||
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
|
||||
{
|
||||
return strbuffer_append_bytes(strbuff, &byte, 1);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,6 +8,8 @@
|
||||
#ifndef STRBUFFER_H
|
||||
#define STRBUFFER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
char *value;
|
||||
size_t length; /* bytes used */
|
||||
@@ -24,7 +26,6 @@ const char *strbuffer_value(const strbuffer_t *strbuff);
|
||||
/* Steal the value and close the strbuffer */
|
||||
char *strbuffer_steal_value(strbuffer_t *strbuff);
|
||||
|
||||
int strbuffer_append(strbuffer_t *strbuff, const char *string);
|
||||
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
|
||||
|
||||
|
||||
@@ -2,12 +2,20 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#ifdef __MINGW32__
|
||||
#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */
|
||||
#endif
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
|
||||
/* need config.h to get the correct snprintf */
|
||||
/* need jansson_private_config.h to get the correct snprintf */
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define strtod __strtod
|
||||
#endif
|
||||
|
||||
#if JSON_HAVE_LOCALECONV
|
||||
@@ -69,7 +77,7 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
value = strtod(strbuffer->value, &end);
|
||||
assert(end == strbuffer->value + strbuffer->length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
|
||||
/* Overflow */
|
||||
return -1;
|
||||
}
|
||||
@@ -78,13 +86,16 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value)
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
|
||||
{
|
||||
int ret;
|
||||
char *start, *end;
|
||||
size_t length;
|
||||
|
||||
ret = snprintf(buffer, size, "%.17g", value);
|
||||
if (precision == 0)
|
||||
precision = 17;
|
||||
|
||||
ret = snprintf(buffer, size, "%.*g", precision, value);
|
||||
if(ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
29
src/utf.c
29
src/utf.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <string.h>
|
||||
#include "utf.h"
|
||||
|
||||
int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
|
||||
{
|
||||
if(codepoint < 0)
|
||||
return -1;
|
||||
@@ -44,7 +44,7 @@ int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int utf8_check_first(char byte)
|
||||
size_t utf8_check_first(char byte)
|
||||
{
|
||||
unsigned char u = (unsigned char)byte;
|
||||
|
||||
@@ -80,9 +80,9 @@ int utf8_check_first(char byte)
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
int32_t value = 0;
|
||||
unsigned char u = (unsigned char)buffer[0];
|
||||
|
||||
@@ -136,12 +136,12 @@ int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
|
||||
{
|
||||
int count;
|
||||
size_t count;
|
||||
int32_t value;
|
||||
|
||||
if(!*buffer)
|
||||
if(!bufsize)
|
||||
return buffer;
|
||||
|
||||
count = utf8_check_first(buffer[0]);
|
||||
@@ -152,7 +152,7 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
value = (unsigned char)buffer[0];
|
||||
else
|
||||
{
|
||||
if(!utf8_check_full(buffer, count, &value))
|
||||
if(count > bufsize || !utf8_check_full(buffer, count, &value))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -162,21 +162,18 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
return buffer + count;
|
||||
}
|
||||
|
||||
int utf8_check_string(const char *string, int length)
|
||||
int utf8_check_string(const char *string, size_t length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(length == -1)
|
||||
length = strlen(string);
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < length; i++)
|
||||
{
|
||||
int count = utf8_check_first(string[i]);
|
||||
size_t count = utf8_check_first(string[i]);
|
||||
if(count == 0)
|
||||
return 0;
|
||||
else if(count > 1)
|
||||
{
|
||||
if(i + count > length)
|
||||
if(count > length - i)
|
||||
return 0;
|
||||
|
||||
if(!utf8_check_full(&string[i], count, NULL))
|
||||
|
||||
14
src/utf.h
14
src/utf.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,19 +9,19 @@
|
||||
#define UTF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
|
||||
|
||||
int utf8_check_first(char byte);
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
|
||||
size_t utf8_check_first(char byte);
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
|
||||
|
||||
int utf8_check_string(const char *string, int length);
|
||||
int utf8_check_string(const char *string, size_t length);
|
||||
|
||||
#endif
|
||||
|
||||
261
src/value.c
261
src/value.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -10,7 +10,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -29,8 +29,10 @@
|
||||
|
||||
/* Work around nonstandard isnan() and isinf() implementations */
|
||||
#ifndef isnan
|
||||
#ifndef __sun
|
||||
static JSON_INLINE int isnan(double x) { return x != x; }
|
||||
#endif
|
||||
#endif
|
||||
#ifndef isinf
|
||||
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
||||
#endif
|
||||
@@ -65,7 +67,6 @@ json_t *json_object(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
object->serial = 0;
|
||||
object->visited = 0;
|
||||
|
||||
return &object->json;
|
||||
@@ -92,7 +93,7 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !json_is_object(json))
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
@@ -113,7 +114,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
}
|
||||
object = json_to_object(json);
|
||||
|
||||
if(hashtable_set(&object->hashtable, key, object->serial++, value))
|
||||
if(hashtable_set(&object->hashtable, key, value))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@@ -124,7 +125,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!key || !utf8_check_string(key, -1))
|
||||
if(!key || !utf8_check_string(key, strlen(key)))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@@ -137,7 +138,7 @@ int json_object_del(json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
@@ -152,9 +153,7 @@ int json_object_clear(json_t *json)
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
|
||||
hashtable_clear(&object->hashtable);
|
||||
object->serial = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -636,33 +635,68 @@ static json_t *json_array_deep_copy(const json_t *array)
|
||||
|
||||
/*** string ***/
|
||||
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
static json_t *string_create(const char *value, size_t len, int own)
|
||||
{
|
||||
char *v;
|
||||
json_string_t *string;
|
||||
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string)
|
||||
return NULL;
|
||||
json_init(&string->json, JSON_STRING);
|
||||
if(own)
|
||||
v = (char *)value;
|
||||
else {
|
||||
v = jsonp_strndup(value, len);
|
||||
if(!v)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string->value = jsonp_strdup(value);
|
||||
if(!string->value) {
|
||||
jsonp_free(string);
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string) {
|
||||
if(!own)
|
||||
jsonp_free(v);
|
||||
return NULL;
|
||||
}
|
||||
json_init(&string->json, JSON_STRING);
|
||||
string->value = v;
|
||||
string->length = len;
|
||||
|
||||
return &string->json;
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_string_nocheck(value);
|
||||
return string_create(value, strlen(value), 0);
|
||||
}
|
||||
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 0);
|
||||
}
|
||||
|
||||
/* this is private; "steal" is not a public API concept */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 1);
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_stringn(value, strlen(value));
|
||||
}
|
||||
|
||||
json_t *json_stringn(const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return NULL;
|
||||
|
||||
return json_stringn_nocheck(value, len);
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *json)
|
||||
@@ -673,7 +707,23 @@ const char *json_string_value(const json_t *json)
|
||||
return json_to_string(json)->value;
|
||||
}
|
||||
|
||||
size_t json_string_length(const json_t *json)
|
||||
{
|
||||
if(!json_is_string(json))
|
||||
return 0;
|
||||
|
||||
return json_to_string(json)->length;
|
||||
}
|
||||
|
||||
int json_string_set_nocheck(json_t *json, const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn_nocheck(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
@@ -681,23 +731,32 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
if(!json_is_string(json) || !value)
|
||||
return -1;
|
||||
|
||||
dup = jsonp_strdup(value);
|
||||
dup = jsonp_strndup(value, len);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
string = json_to_string(json);
|
||||
jsonp_free(string->value);
|
||||
string->value = dup;
|
||||
string->length = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_string_set(json_t *json, const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_set_nocheck(json, value);
|
||||
return json_string_setn(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, len);
|
||||
}
|
||||
|
||||
static void json_delete_string(json_string_t *string)
|
||||
@@ -708,12 +767,25 @@ static void json_delete_string(json_string_t *string)
|
||||
|
||||
static int json_string_equal(json_t *string1, json_t *string2)
|
||||
{
|
||||
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||
json_string_t *s1, *s2;
|
||||
|
||||
if(!json_is_string(string1) || !json_is_string(string2))
|
||||
return 0;
|
||||
|
||||
s1 = json_to_string(string1);
|
||||
s2 = json_to_string(string2);
|
||||
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
|
||||
}
|
||||
|
||||
static json_t *json_string_copy(const json_t *string)
|
||||
{
|
||||
return json_string_nocheck(json_string_value(string));
|
||||
json_string_t *s;
|
||||
|
||||
if(!json_is_string(string))
|
||||
return NULL;
|
||||
|
||||
s = json_to_string(string);
|
||||
return json_stringn_nocheck(s->value, s->length);
|
||||
}
|
||||
|
||||
|
||||
@@ -856,20 +928,28 @@ json_t *json_null(void)
|
||||
|
||||
void json_delete(json_t *json)
|
||||
{
|
||||
if(json_is_object(json))
|
||||
json_delete_object(json_to_object(json));
|
||||
if (!json)
|
||||
return;
|
||||
|
||||
else if(json_is_array(json))
|
||||
json_delete_array(json_to_array(json));
|
||||
|
||||
else if(json_is_string(json))
|
||||
json_delete_string(json_to_string(json));
|
||||
|
||||
else if(json_is_integer(json))
|
||||
json_delete_integer(json_to_integer(json));
|
||||
|
||||
else if(json_is_real(json))
|
||||
json_delete_real(json_to_real(json));
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_OBJECT:
|
||||
json_delete_object(json_to_object(json));
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
json_delete_array(json_to_array(json));
|
||||
break;
|
||||
case JSON_STRING:
|
||||
json_delete_string(json_to_string(json));
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
json_delete_integer(json_to_integer(json));
|
||||
break;
|
||||
case JSON_REAL:
|
||||
json_delete_real(json_to_real(json));
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* json_delete is not called for true, false or null */
|
||||
}
|
||||
@@ -889,22 +969,20 @@ int json_equal(json_t *json1, json_t *json2)
|
||||
if(json1 == json2)
|
||||
return 1;
|
||||
|
||||
if(json_is_object(json1))
|
||||
return json_object_equal(json1, json2);
|
||||
|
||||
if(json_is_array(json1))
|
||||
return json_array_equal(json1, json2);
|
||||
|
||||
if(json_is_string(json1))
|
||||
return json_string_equal(json1, json2);
|
||||
|
||||
if(json_is_integer(json1))
|
||||
return json_integer_equal(json1, json2);
|
||||
|
||||
if(json_is_real(json1))
|
||||
return json_real_equal(json1, json2);
|
||||
|
||||
return 0;
|
||||
switch(json_typeof(json1)) {
|
||||
case JSON_OBJECT:
|
||||
return json_object_equal(json1, json2);
|
||||
case JSON_ARRAY:
|
||||
return json_array_equal(json1, json2);
|
||||
case JSON_STRING:
|
||||
return json_string_equal(json1, json2);
|
||||
case JSON_INTEGER:
|
||||
return json_integer_equal(json1, json2);
|
||||
case JSON_REAL:
|
||||
return json_real_equal(json1, json2);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -915,23 +993,24 @@ json_t *json_copy(json_t *json)
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
if(json_is_object(json))
|
||||
return json_object_copy(json);
|
||||
|
||||
if(json_is_array(json))
|
||||
return json_array_copy(json);
|
||||
|
||||
if(json_is_string(json))
|
||||
return json_string_copy(json);
|
||||
|
||||
if(json_is_integer(json))
|
||||
return json_integer_copy(json);
|
||||
|
||||
if(json_is_real(json))
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return json;
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_OBJECT:
|
||||
return json_object_copy(json);
|
||||
case JSON_ARRAY:
|
||||
return json_array_copy(json);
|
||||
case JSON_STRING:
|
||||
return json_string_copy(json);
|
||||
case JSON_INTEGER:
|
||||
return json_integer_copy(json);
|
||||
case JSON_REAL:
|
||||
return json_real_copy(json);
|
||||
case JSON_TRUE:
|
||||
case JSON_FALSE:
|
||||
case JSON_NULL:
|
||||
return json;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -941,26 +1020,26 @@ json_t *json_deep_copy(const json_t *json)
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
if(json_is_object(json))
|
||||
return json_object_deep_copy(json);
|
||||
|
||||
if(json_is_array(json))
|
||||
return json_array_deep_copy(json);
|
||||
|
||||
/* for the rest of the types, deep copying doesn't differ from
|
||||
shallow copying */
|
||||
|
||||
if(json_is_string(json))
|
||||
return json_string_copy(json);
|
||||
|
||||
if(json_is_integer(json))
|
||||
return json_integer_copy(json);
|
||||
|
||||
if(json_is_real(json))
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return (json_t *)json;
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_OBJECT:
|
||||
return json_object_deep_copy(json);
|
||||
case JSON_ARRAY:
|
||||
return json_array_deep_copy(json);
|
||||
/* for the rest of the types, deep copying doesn't differ from
|
||||
shallow copying */
|
||||
case JSON_STRING:
|
||||
return json_string_copy(json);
|
||||
case JSON_INTEGER:
|
||||
return json_integer_copy(json);
|
||||
case JSON_REAL:
|
||||
return json_real_copy(json);
|
||||
case JSON_TRUE:
|
||||
case JSON_FALSE:
|
||||
case JSON_NULL:
|
||||
return (json_t *)json;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
4
test/.gitignore
vendored
4
test/.gitignore
vendored
@@ -15,3 +15,7 @@ suites/api/test_pack
|
||||
suites/api/test_simple
|
||||
suites/api/test_unpack
|
||||
suites/api/test_load_callback
|
||||
run-suites.log
|
||||
run-suites.trs
|
||||
test-suite.log
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -39,6 +39,7 @@ struct config {
|
||||
int use_env;
|
||||
int have_hashseed;
|
||||
int hashseed;
|
||||
int precision;
|
||||
} conf;
|
||||
|
||||
#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t')
|
||||
@@ -108,6 +109,8 @@ static void read_conf(FILE *conffile)
|
||||
conf.preserve_order = atoi(val);
|
||||
if (!strcmp(line, "JSON_SORT_KEYS"))
|
||||
conf.sort_keys = atoi(val);
|
||||
if (!strcmp(line, "JSON_REAL_PRECISION"))
|
||||
conf.precision = atoi(val);
|
||||
if (!strcmp(line, "STRIP"))
|
||||
conf.strip = atoi(val);
|
||||
if (!strcmp(line, "HASHSEED")) {
|
||||
@@ -176,11 +179,11 @@ int use_conf(char *test_path)
|
||||
fclose(conffile);
|
||||
}
|
||||
|
||||
if (conf.indent < 0 || conf.indent > 255) {
|
||||
if (conf.indent < 0 || conf.indent > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent);
|
||||
fclose(infile);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (conf.indent)
|
||||
flags |= JSON_INDENT(conf.indent);
|
||||
|
||||
@@ -196,6 +199,15 @@ int use_conf(char *test_path)
|
||||
if (conf.sort_keys)
|
||||
flags |= JSON_SORT_KEYS;
|
||||
|
||||
if (conf.precision < 0 || conf.precision > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
|
||||
conf.precision);
|
||||
fclose(infile);
|
||||
return 2;
|
||||
}
|
||||
if (conf.precision)
|
||||
flags |= JSON_REAL_PRECISION(conf.precision);
|
||||
|
||||
if (conf.have_hashseed)
|
||||
json_object_seed(conf.hashseed);
|
||||
|
||||
@@ -245,7 +257,7 @@ static int getenv_int(const char *name)
|
||||
|
||||
int use_env()
|
||||
{
|
||||
int indent;
|
||||
int indent, precision;
|
||||
size_t flags = 0;
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
@@ -258,11 +270,10 @@ int use_env()
|
||||
#endif
|
||||
|
||||
indent = getenv_int("JSON_INDENT");
|
||||
if(indent < 0 || indent > 255) {
|
||||
if(indent < 0 || indent > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(indent > 0)
|
||||
flags |= JSON_INDENT(indent);
|
||||
|
||||
@@ -278,23 +289,35 @@ int use_env()
|
||||
if(getenv_int("JSON_SORT_KEYS"))
|
||||
flags |= JSON_SORT_KEYS;
|
||||
|
||||
precision = getenv_int("JSON_REAL_PRECISION");
|
||||
if(precision < 0 || precision > 31) {
|
||||
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
|
||||
precision);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(getenv("HASHSEED"))
|
||||
json_object_seed(getenv_int("HASHSEED"));
|
||||
|
||||
if(precision > 0)
|
||||
flags |= JSON_REAL_PRECISION(precision);
|
||||
|
||||
if(getenv_int("STRIP")) {
|
||||
/* Load to memory, strip leading and trailing whitespace */
|
||||
size_t size = 0, used = 0;
|
||||
char *buffer = NULL;
|
||||
char *buffer = NULL, *buf_ck = NULL;
|
||||
|
||||
while(1) {
|
||||
size_t count;
|
||||
|
||||
size = (size == 0 ? 128 : size * 2);
|
||||
buffer = realloc(buffer, size);
|
||||
if(!buffer) {
|
||||
buf_ck = realloc(buffer, size);
|
||||
if(!buf_ck) {
|
||||
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
buffer = buf_ck;
|
||||
|
||||
count = fread(buffer + used, 1, size - used, stdin);
|
||||
if(count < size - used) {
|
||||
|
||||
@@ -34,15 +34,15 @@ failed=0
|
||||
for suite in $SUITES; do
|
||||
echo "Suite: $suite"
|
||||
if $suites_srcdir/$suite/run $suite; then
|
||||
passed=$(($passed+1))
|
||||
passed=`expr $passed + 1`
|
||||
else
|
||||
failed=$(($failed+1))
|
||||
failed=`expr $failed + 1`
|
||||
[ $STOP -eq 1 ] && break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
echo "$failed of $((passed+failed)) test suites failed"
|
||||
echo "$failed of `expr $passed + $failed` test suites failed"
|
||||
exit 1
|
||||
else
|
||||
echo "$passed test suites passed"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -232,6 +232,9 @@ static void test_copy_object(void)
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
const char *keys[] = {"foo", "a", "b", "c"};
|
||||
int i;
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
@@ -247,6 +250,7 @@ static void test_copy_object(void)
|
||||
if(!json_equal(copy, object))
|
||||
fail("copying an object produces an inequal copy");
|
||||
|
||||
i = 0;
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
@@ -258,9 +262,13 @@ static void test_copy_object(void)
|
||||
value2 = json_object_get(copy, key);
|
||||
|
||||
if(value1 != value2)
|
||||
fail("deep copying an object modifies its items");
|
||||
fail("copying an object modifies its items");
|
||||
|
||||
if (strcmp(key, keys[i]) != 0)
|
||||
fail("copying an object doesn't preserve key order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
@@ -272,6 +280,9 @@ static void test_deep_copy_object(void)
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
const char *keys[] = {"foo", "a", "b", "c"};
|
||||
int i;
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
@@ -287,6 +298,7 @@ static void test_deep_copy_object(void)
|
||||
if(!json_equal(copy, object))
|
||||
fail("deep copying an object produces an inequal copy");
|
||||
|
||||
i = 0;
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
@@ -300,7 +312,11 @@ static void test_deep_copy_object(void)
|
||||
if(value1 == value2)
|
||||
fail("deep copying an object doesn't copy its items");
|
||||
|
||||
if (strcmp(key, keys[i]) != 0)
|
||||
fail("deep copying an object doesn't preserve key order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "jansson_private_config.h"
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "util.h"
|
||||
|
||||
static int encode_null_callback(const char *buffer, size_t size, void *data)
|
||||
@@ -22,9 +27,17 @@ static void encode_null()
|
||||
if(json_dumps(NULL, JSON_ENCODE_ANY) != NULL)
|
||||
fail("json_dumps didn't fail for NULL");
|
||||
|
||||
if(json_dumpb(NULL, NULL, 0, JSON_ENCODE_ANY) != 0)
|
||||
fail("json_dumps didn't fail for NULL");
|
||||
|
||||
if(json_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1)
|
||||
fail("json_dumpf didn't fail for NULL");
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
if(json_dumpfd(NULL, STDERR_FILENO, JSON_ENCODE_ANY) != -1)
|
||||
fail("json_dumpfd didn't fail for NULL");
|
||||
#endif
|
||||
|
||||
/* Don't test json_dump_file to avoid creating a file */
|
||||
|
||||
if(json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1)
|
||||
@@ -124,14 +137,15 @@ static void encode_other_than_array_or_object()
|
||||
* succeed if the JSON_ENCODE_ANY flag is used */
|
||||
|
||||
json_t *json;
|
||||
FILE *fp = NULL;
|
||||
char *result;
|
||||
|
||||
json = json_string("foo");
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded a string!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
if(json_dumpf(json, NULL, 0) == 0)
|
||||
fail("json_dumpf encoded a string!");
|
||||
if(json_dumpfd(json, -1, 0) == 0)
|
||||
fail("json_dumpfd encoded a string!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "\"foo\"") != 0)
|
||||
@@ -143,8 +157,10 @@ static void encode_other_than_array_or_object()
|
||||
json = json_integer(42);
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded an integer!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
if(json_dumpf(json, NULL, 0) == 0)
|
||||
fail("json_dumpf encoded an integer!");
|
||||
if(json_dumpfd(json, -1, 0) == 0)
|
||||
fail("json_dumpfd encoded an integer!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "42") != 0)
|
||||
@@ -180,6 +196,121 @@ static void escape_slashes()
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void encode_nul_byte()
|
||||
{
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_stringn("nul byte \0 in string", 20);
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || memcmp(result, "\"nul byte \\u0000 in string\"", 27))
|
||||
fail("json_dumps failed to dump an embedded NUL byte");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void dump_file()
|
||||
{
|
||||
json_t *json;
|
||||
int result;
|
||||
|
||||
result = json_dump_file(NULL, "", 0);
|
||||
if (result != -1)
|
||||
fail("json_dump_file succeeded with invalid args");
|
||||
|
||||
json = json_object();
|
||||
result = json_dump_file(json, "json_dump_file.json", 0);
|
||||
if (result != 0)
|
||||
fail("json_dump_file failed");
|
||||
|
||||
json_decref(json);
|
||||
remove("json_dump_file.json");
|
||||
}
|
||||
|
||||
static void dumpb()
|
||||
{
|
||||
char buf[2];
|
||||
json_t *obj;
|
||||
size_t size;
|
||||
|
||||
obj = json_object();
|
||||
|
||||
size = json_dumpb(obj, buf, sizeof(buf), 0);
|
||||
if(size != 2 || strncmp(buf, "{}", 2))
|
||||
fail("json_dumpb failed");
|
||||
|
||||
json_decref(obj);
|
||||
obj = json_pack("{s:s}", "foo", "bar");
|
||||
|
||||
size = json_dumpb(obj, buf, sizeof(buf), JSON_COMPACT);
|
||||
if(size != 13)
|
||||
fail("json_dumpb size check failed");
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void dumpfd()
|
||||
{
|
||||
#ifdef HAVE_UNISTD_H
|
||||
int fds[2] = {-1, -1};
|
||||
json_t *a, *b;
|
||||
|
||||
if(pipe(fds))
|
||||
fail("pipe() failed");
|
||||
|
||||
a = json_pack("{s:s}", "foo", "bar");
|
||||
|
||||
if(json_dumpfd(a, fds[1], 0))
|
||||
fail("json_dumpfd() failed");
|
||||
close(fds[1]);
|
||||
|
||||
b = json_loadfd(fds[0], 0, NULL);
|
||||
if (!b)
|
||||
fail("json_loadfd() failed");
|
||||
close(fds[0]);
|
||||
|
||||
if (!json_equal(a, b))
|
||||
fail("json_equal() failed for fd test");
|
||||
|
||||
json_decref(a);
|
||||
json_decref(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void embed()
|
||||
{
|
||||
static const char *plains[] = {
|
||||
"{\"bar\":[],\"foo\":{}}",
|
||||
"[[],{}]",
|
||||
"{}",
|
||||
"[]",
|
||||
NULL
|
||||
};
|
||||
|
||||
size_t i;
|
||||
|
||||
for(i = 0; plains[i]; i++) {
|
||||
const char *plain = plains[i];
|
||||
json_t *parse = NULL;
|
||||
char *embed = NULL;
|
||||
size_t psize = 0;
|
||||
size_t esize = 0;
|
||||
|
||||
psize = strlen(plain) - 2;
|
||||
embed = calloc(1, psize);
|
||||
parse = json_loads(plain, 0, NULL);
|
||||
esize = json_dumpb(parse, embed, psize,
|
||||
JSON_COMPACT | JSON_SORT_KEYS | JSON_EMBED);
|
||||
json_decref(parse);
|
||||
if(esize != psize)
|
||||
fail("json_dumpb(JSON_EMBED) returned an invalid size");
|
||||
if(strncmp(plain + 1, embed, esize) != 0)
|
||||
fail("json_dumps(JSON_EMBED) returned an invalid value");
|
||||
free(embed);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
encode_null();
|
||||
@@ -187,4 +318,9 @@ static void run_tests()
|
||||
circular_references();
|
||||
encode_other_than_array_or_object();
|
||||
escape_slashes();
|
||||
encode_nul_byte();
|
||||
dump_file();
|
||||
dumpb();
|
||||
dumpfd();
|
||||
embed();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -34,6 +34,20 @@ static void file_not_found()
|
||||
fail("json_load_file returned an invalid error message");
|
||||
}
|
||||
|
||||
static void very_long_file_name() {
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, &error);
|
||||
if(json)
|
||||
fail("json_load_file returned non-NULL for a nonexistent file");
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
|
||||
if (strncmp(error.source, "...aaa", 6) != 0)
|
||||
fail("error source was set incorrectly");
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
{
|
||||
json_error_t error;
|
||||
@@ -97,6 +111,8 @@ static void decode_int_as_real()
|
||||
json_int_t expected;
|
||||
#endif
|
||||
|
||||
char big[311];
|
||||
|
||||
json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
|
||||
if (!json || !json_is_real(json) || json_real_value(json) != 42.0)
|
||||
fail("json_load decode int as real failed - int");
|
||||
@@ -113,6 +129,38 @@ static void decode_int_as_real()
|
||||
fail("json_load decode int as real failed - expected imprecision");
|
||||
json_decref(json);
|
||||
#endif
|
||||
|
||||
/* 1E309 overflows. Here we create 1E309 as a decimal number, i.e.
|
||||
1000...(309 zeroes)...0. */
|
||||
big[0] = '1';
|
||||
memset(big + 1, '0', 309);
|
||||
big[310] = '\0';
|
||||
|
||||
json = json_loads(big, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
|
||||
if (json || strcmp(error.text, "real number overflow") != 0)
|
||||
fail("json_load decode int as real failed - expected overflow");
|
||||
json_decref(json);
|
||||
|
||||
}
|
||||
|
||||
static void allow_nul()
|
||||
{
|
||||
const char *text = "\"nul byte \\u0000 in string\"";
|
||||
const char *expected = "nul byte \0 in string";
|
||||
size_t len = 20;
|
||||
json_t *json;
|
||||
|
||||
json = json_loads(text, JSON_ALLOW_NUL | JSON_DECODE_ANY, NULL);
|
||||
if(!json || !json_is_string(json))
|
||||
fail("unable to decode embedded NUL byte");
|
||||
|
||||
if(json_string_length(json) != len)
|
||||
fail("decoder returned wrong string length");
|
||||
|
||||
if(memcmp(json_string_value(json), expected, len + 1))
|
||||
fail("decoder returned wrong string content");
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void load_wrong_args()
|
||||
@@ -132,9 +180,13 @@ static void load_wrong_args()
|
||||
if (json)
|
||||
fail("json_loadf should return NULL if the first argument is NULL");
|
||||
|
||||
json = json_loadfd(-1, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadfd should return NULL if the first argument is < 0");
|
||||
|
||||
json = json_load_file(NULL, 0, &error);
|
||||
if (json)
|
||||
fail("json_loadf should return NULL if the first argument is NULL");
|
||||
fail("json_load_file should return NULL if the first argument is NULL");
|
||||
}
|
||||
|
||||
static void position()
|
||||
@@ -157,10 +209,12 @@ static void position()
|
||||
static void run_tests()
|
||||
{
|
||||
file_not_found();
|
||||
very_long_file_name();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
decode_any();
|
||||
decode_int_as_real();
|
||||
allow_nul();
|
||||
load_wrong_args();
|
||||
position();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
static int malloc_called = 0;
|
||||
static int free_called = 0;
|
||||
static size_t malloc_used = 0;
|
||||
|
||||
/* helper */
|
||||
/* helpers */
|
||||
static void create_and_free_complex_object()
|
||||
{
|
||||
json_t *obj;
|
||||
|
||||
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]",
|
||||
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]}",
|
||||
"foo", 42,
|
||||
"bar",
|
||||
"baz", 1,
|
||||
@@ -22,6 +23,21 @@ static void create_and_free_complex_object()
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void create_and_free_object_with_oom()
|
||||
{
|
||||
int i;
|
||||
char key[4];
|
||||
json_t *obj = json_object();
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
snprintf(key, sizeof key, "%d", i);
|
||||
json_object_set_new(obj, key, json_integer(i));
|
||||
}
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void *my_malloc(size_t size)
|
||||
{
|
||||
malloc_called = 1;
|
||||
@@ -36,14 +52,45 @@ static void my_free(void *ptr)
|
||||
|
||||
static void test_simple()
|
||||
{
|
||||
json_malloc_t mfunc = NULL;
|
||||
json_free_t ffunc = NULL;
|
||||
|
||||
json_set_alloc_funcs(my_malloc, my_free);
|
||||
json_get_alloc_funcs(&mfunc, &ffunc);
|
||||
create_and_free_complex_object();
|
||||
|
||||
if(malloc_called != 1 || free_called != 1)
|
||||
if (malloc_called != 1 || free_called != 1
|
||||
|| mfunc != my_malloc || ffunc != my_free)
|
||||
fail("Custom allocation failed");
|
||||
}
|
||||
|
||||
|
||||
static void *oom_malloc(size_t size)
|
||||
{
|
||||
if (malloc_used + size > 800)
|
||||
return NULL;
|
||||
|
||||
malloc_used += size;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void oom_free(void *ptr)
|
||||
{
|
||||
free_called++;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void test_oom()
|
||||
{
|
||||
free_called = 0;
|
||||
json_set_alloc_funcs(oom_malloc, oom_free);
|
||||
create_and_free_object_with_oom();
|
||||
|
||||
if (free_called == 0)
|
||||
fail("Allocation with OOM failed");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Test the secure memory functions code given in the API reference
|
||||
documentation, but by using plain memset instead of
|
||||
@@ -79,4 +126,5 @@ static void run_tests()
|
||||
{
|
||||
test_simple();
|
||||
test_secure_funcs();
|
||||
test_oom();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,6 +9,34 @@
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
#ifdef INFINITY
|
||||
// This test triggers "warning C4756: overflow in constant arithmetic"
|
||||
// in Visual Studio. This warning is triggered here by design, so disable it.
|
||||
// (This can only be done on function level so we keep these tests separate)
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning (disable: 4756)
|
||||
#endif
|
||||
static void test_inifity()
|
||||
{
|
||||
json_t *real = json_real(INFINITY);
|
||||
if (real != NULL)
|
||||
fail("could construct a real from Inf");
|
||||
|
||||
real = json_real(1.0);
|
||||
if (json_real_set(real, INFINITY) != -1)
|
||||
fail("could set a real to Inf");
|
||||
|
||||
if (json_real_value(real) != 1.0)
|
||||
fail("real value changed unexpectedly");
|
||||
|
||||
json_decref(real);
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
#endif // INFINITY
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *integer, *real;
|
||||
@@ -57,17 +85,6 @@ static void run_tests()
|
||||
#endif
|
||||
|
||||
#ifdef INFINITY
|
||||
real = json_real(INFINITY);
|
||||
if(real != NULL)
|
||||
fail("could construct a real from Inf");
|
||||
|
||||
real = json_real(1.0);
|
||||
if(json_real_set(real, INFINITY) != -1)
|
||||
fail("could set a real to Inf");
|
||||
|
||||
if(json_real_value(real) != 1.0)
|
||||
fail("real value changed unexpectedly");
|
||||
|
||||
json_decref(real);
|
||||
test_inifity();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -139,6 +139,32 @@ static void test_update()
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_set_many_keys()
|
||||
{
|
||||
json_t *object, *value;
|
||||
const char *keys = "abcdefghijklmnopqrstuvwxyz";
|
||||
char buf[2];
|
||||
size_t i;
|
||||
|
||||
object = json_object();
|
||||
if (!object)
|
||||
fail("unable to create object");
|
||||
|
||||
value = json_string("a");
|
||||
if (!value)
|
||||
fail("unable to create string");
|
||||
|
||||
buf[1] = '\0';
|
||||
for (i = 0; i < strlen(keys); i++) {
|
||||
buf[0] = keys[i];
|
||||
if (json_object_set(object, buf, value))
|
||||
fail("unable to set object key");
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
json_decref(value);
|
||||
}
|
||||
|
||||
static void test_conditional_updates()
|
||||
{
|
||||
json_t *object, *other;
|
||||
@@ -249,11 +275,7 @@ 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))
|
||||
@@ -266,7 +288,7 @@ static void test_iterators()
|
||||
foo = json_string("foo");
|
||||
bar = json_string("bar");
|
||||
baz = json_string("baz");
|
||||
if(!object || !foo || !bar || !bar)
|
||||
if(!object || !foo || !bar || !baz)
|
||||
fail("unable to create values");
|
||||
|
||||
if(json_object_iter_next(object, NULL))
|
||||
@@ -280,50 +302,30 @@ static void test_iterators()
|
||||
iter = json_object_iter(object);
|
||||
if(!iter)
|
||||
fail("unable to get iterator");
|
||||
iter_keys[0] = json_object_iter_key(iter);
|
||||
iter_values[0] = json_object_iter_value(iter);
|
||||
if (strcmp(json_object_iter_key(iter), "a") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != foo)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
iter_keys[1] = json_object_iter_key(iter);
|
||||
iter_values[1] = json_object_iter_value(iter);
|
||||
if (strcmp(json_object_iter_key(iter), "b") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != bar)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
iter_keys[2] = json_object_iter_key(iter);
|
||||
iter_values[2] = json_object_iter_value(iter);
|
||||
if (strcmp(json_object_iter_key(iter), "c") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != baz)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
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");
|
||||
|
||||
@@ -374,6 +376,12 @@ static void test_misc()
|
||||
if(!json_object_set(object, NULL, string))
|
||||
fail("able to set NULL key");
|
||||
|
||||
if(json_object_del(object, "a"))
|
||||
fail("unable to del the only key");
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_object_set(object, "a", NULL))
|
||||
fail("able to set NULL value");
|
||||
|
||||
@@ -513,15 +521,35 @@ static void test_object_foreach()
|
||||
json_decref(object2);
|
||||
}
|
||||
|
||||
static void test_object_foreach_safe()
|
||||
{
|
||||
const char *key;
|
||||
void *tmp;
|
||||
json_t *object, *value;
|
||||
|
||||
object = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
|
||||
|
||||
json_object_foreach_safe(object, tmp, key, value) {
|
||||
json_object_del(object, key);
|
||||
}
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("json_object_foreach_safe failed to iterate all key-value pairs");
|
||||
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
test_update();
|
||||
test_set_many_keys();
|
||||
test_conditional_updates();
|
||||
test_circular();
|
||||
test_set_nocheck();
|
||||
test_iterators();
|
||||
test_preserve_order();
|
||||
test_object_foreach();
|
||||
test_object_foreach_safe();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <jansson_config.h>
|
||||
@@ -83,7 +83,23 @@ static void run_tests()
|
||||
fail("json_pack string refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length */
|
||||
/* nullable string (defined case) */
|
||||
value = json_pack("s?", "test");
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack nullable string (defined case) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack nullable string (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* nullable string (NULL case) */
|
||||
value = json_pack("s?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack nullable string (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack nullable string (NULL case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (int) */
|
||||
value = json_pack("s#", "test asdf", 4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length failed");
|
||||
@@ -91,14 +107,30 @@ static void run_tests()
|
||||
fail("json_pack string and length refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length, non-NUL terminated string */
|
||||
value = json_pack("s#", buffer, 4);
|
||||
/* string and length (size_t) */
|
||||
value = json_pack("s%", "test asdf", (size_t)4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (int), non-NUL terminated string */
|
||||
value = json_pack("s#", buffer, 4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length (int) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length (int) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (size_t), non-NUL terminated string */
|
||||
value = json_pack("s%", buffer, (size_t)4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length (size_t) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length (size_t) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation */
|
||||
value = json_pack("s++", "te", "st", "ing");
|
||||
if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
|
||||
@@ -107,12 +139,20 @@ static void run_tests()
|
||||
fail("json_pack string concatenation refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation and length */
|
||||
/* string concatenation and length (int) */
|
||||
value = json_pack("s#+#+", "test", 1, "test", 2, "test");
|
||||
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
|
||||
fail("json_pack string concatenation and length failed");
|
||||
fail("json_pack string concatenation and length (int) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string concatenation and length refcount failed");
|
||||
fail("json_pack string concatenation and length (int) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation and length (size_t) */
|
||||
value = json_pack("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
|
||||
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
|
||||
fail("json_pack string concatenation and length (size_t) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string concatenation and length (size_t) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* empty object */
|
||||
@@ -139,6 +179,22 @@ static void run_tests()
|
||||
fail("json_pack integer refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* non-incref'd nullable object (defined case) */
|
||||
value = json_pack("o?", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack nullable object (defined case) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack nullable object (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* non-incref'd nullable object (NULL case) */
|
||||
value = json_pack("o?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack nullable object (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack nullable object (NULL case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* incref'd object */
|
||||
value = json_pack("O", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
@@ -148,6 +204,22 @@ static void run_tests()
|
||||
json_decref(value);
|
||||
json_decref(value);
|
||||
|
||||
/* incref'd nullable object (defined case) */
|
||||
value = json_pack("O?", json_integer(1));
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 1)
|
||||
fail("json_pack incref'd nullable object (defined case) failed");
|
||||
if(value->refcount != (size_t)2)
|
||||
fail("json_pack incref'd nullable object (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
json_decref(value);
|
||||
|
||||
/* incref'd nullable object (NULL case) */
|
||||
value = json_pack("O?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack incref'd nullable object (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack incref'd nullable object (NULL case) refcount failed");
|
||||
|
||||
/* simple object */
|
||||
value = json_pack("{s:[]}", "foo");
|
||||
if(!json_is_object(value) || json_object_size(value) != 1)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -27,6 +27,8 @@ static void run_tests()
|
||||
value = json_boolean(0);
|
||||
if(!json_is_false(value))
|
||||
fail("json_boolean(0) failed");
|
||||
if(json_boolean_value(value) != 0)
|
||||
fail("json_boolean_value failed");
|
||||
json_decref(value);
|
||||
|
||||
|
||||
@@ -72,11 +74,22 @@ static void run_tests()
|
||||
fail("json_string failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 3)
|
||||
fail("invalid string length");
|
||||
|
||||
if(json_string_set(value, "bar"))
|
||||
if(json_string_set(value, "barr"))
|
||||
fail("json_string_set failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
if(strcmp(json_string_value(value), "barr"))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 4)
|
||||
fail("invalid string length");
|
||||
|
||||
if(json_string_setn(value, "hi\0ho", 5))
|
||||
fail("json_string_set failed");
|
||||
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 5)
|
||||
fail("invalid string length");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
@@ -94,11 +107,22 @@ static void run_tests()
|
||||
fail("json_string_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 3)
|
||||
fail("invalid string length");
|
||||
|
||||
if(json_string_set_nocheck(value, "bar"))
|
||||
if(json_string_set_nocheck(value, "barr"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
if(strcmp(json_string_value(value), "barr"))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 4)
|
||||
fail("invalid string length");
|
||||
|
||||
if(json_string_setn_nocheck(value, "hi\0ho", 5))
|
||||
fail("json_string_set failed");
|
||||
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 5)
|
||||
fail("invalid string length");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
@@ -108,11 +132,15 @@ static void run_tests()
|
||||
fail("json_string_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "qu\xff"))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 3)
|
||||
fail("invalid string length");
|
||||
|
||||
if(json_string_set_nocheck(value, "\xfd\xfe\xff"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "\xfd\xfe\xff"))
|
||||
fail("invalid string value");
|
||||
if (json_string_length(value) != 3)
|
||||
fail("invalid string length");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
@@ -196,4 +224,17 @@ static void run_tests()
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
|
||||
#ifdef json_auto_t
|
||||
value = json_string("foo");
|
||||
{
|
||||
json_auto_t *test = json_incref(value);
|
||||
/* Use test so GCC doesn't complain it is unused. */
|
||||
if(!json_is_string(test))
|
||||
fail("value type check failed");
|
||||
}
|
||||
if(value->refcount != 1)
|
||||
fail("automatic decrement failed");
|
||||
json_decref(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
@@ -17,6 +17,7 @@ static void run_tests()
|
||||
int i1, i2, i3;
|
||||
json_int_t I1;
|
||||
int rv;
|
||||
size_t z;
|
||||
double f;
|
||||
char *s;
|
||||
|
||||
@@ -81,6 +82,13 @@ static void run_tests()
|
||||
fail("json_unpack string failed");
|
||||
json_decref(j);
|
||||
|
||||
/* string with length (size_t) */
|
||||
j = json_string("foo");
|
||||
rv = json_unpack(j, "s%", &s, &z);
|
||||
if(rv || strcmp(s, "foo") || z != 3)
|
||||
fail("json_unpack string with length (size_t) failed");
|
||||
json_decref(j);
|
||||
|
||||
/* empty object */
|
||||
j = json_object();
|
||||
if(json_unpack(j, "{}"))
|
||||
@@ -290,10 +298,16 @@ static void run_tests()
|
||||
json_decref(j);
|
||||
|
||||
/* Unpack the same item twice */
|
||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||
j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1);
|
||||
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
|
||||
fail("json_unpack object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
|
||||
{
|
||||
const char *possible_errors[] = {
|
||||
"2 object item(s) left unpacked: baz, quux",
|
||||
"2 object item(s) left unpacked: quux, baz"
|
||||
};
|
||||
check_errors(possible_errors, 2, "<validation>", 1, 10, 10);
|
||||
}
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
|
||||
@@ -327,7 +341,7 @@ static void run_tests()
|
||||
j = json_pack("{s{snsn}}", "foo", "bar", "baz");
|
||||
if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar"))
|
||||
fail("json_unpack nested object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 7, 7);
|
||||
check_error("1 object item(s) left unpacked: baz", "<validation>", 1, 7, 7);
|
||||
json_decref(j);
|
||||
|
||||
/* Error in nested array */
|
||||
@@ -370,4 +384,23 @@ static void run_tests()
|
||||
if(i1 != 42)
|
||||
fail("json_unpack failed to unpack");
|
||||
json_decref(j);
|
||||
|
||||
/* Combine ? and ! */
|
||||
j = json_pack("{si}", "foo", 42);
|
||||
i1 = i2 = 0;
|
||||
if(json_unpack(j, "{sis?i!}", "foo", &i1, "bar", &i2))
|
||||
fail("json_unpack failed for optional values with strict mode");
|
||||
if(i1 != 42)
|
||||
fail("json_unpack failed to unpack");
|
||||
if(i2 != 0)
|
||||
fail("json_unpack failed to unpack");
|
||||
json_decref(j);
|
||||
|
||||
/* But don't compensate a missing key with an optional one. */
|
||||
j = json_pack("{sisi}", "foo", 42, "baz", 43);
|
||||
i1 = i2 = i3 = 0;
|
||||
if(!json_unpack_ex(j, &error, 0, "{sis?i!}", "foo", &i1, "bar", &i2))
|
||||
fail("json_unpack failed for optional values with strict mode and compensation");
|
||||
check_error("1 object item(s) left unpacked: baz", "<validation>", 1, 8, 8);
|
||||
json_decref(j);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,7 +9,7 @@
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
|
||||
#define failhdr fprintf(stderr, "%s:%d: ", __FILE__, __LINE__)
|
||||
|
||||
#define fail(msg) \
|
||||
do { \
|
||||
@@ -30,11 +30,22 @@
|
||||
} while(0)
|
||||
|
||||
/* Assumes json_error_t error */
|
||||
#define check_error(text_, source_, line_, column_, position_) \
|
||||
#define check_errors(texts_, num_, source_, line_, column_, position_) \
|
||||
do { \
|
||||
if(strcmp(error.text, text_) != 0) { \
|
||||
int i_, found_ = 0; \
|
||||
for(i_ = 0; i_ < num_; i_++) { \
|
||||
if(strcmp(error.text, texts_[i_]) == 0) { \
|
||||
found_ = 1; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if (!found_) { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
|
||||
if (num_ == 1) { \
|
||||
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, texts_[0]); \
|
||||
} else { \
|
||||
fprintf(stderr, "text: \"%s\" does not match\n", error.text); \
|
||||
} \
|
||||
exit(1); \
|
||||
} \
|
||||
if(strcmp(error.source, source_) != 0) { \
|
||||
@@ -61,6 +72,11 @@
|
||||
} while(0)
|
||||
|
||||
|
||||
/* Assumes json_error_t error */
|
||||
#define check_error(text_, source_, line_, column_, position_) \
|
||||
check_errors(&text_, 1, source_, line_, column_, position_)
|
||||
|
||||
|
||||
static void run_tests();
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -1 +1 @@
|
||||
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
|
||||
["foo", "\u00E5 \u00E4 \u00F6", "foo \u00E5\u00E4", "\u00E5\u00E4 foo", "\u00E5 foo \u00E4", "clef g: \uD834\uDD1E"]
|
||||
2
test/suites/encoding-flags/real-precision/env
Normal file
2
test/suites/encoding-flags/real-precision/env
Normal file
@@ -0,0 +1,2 @@
|
||||
JSON_REAL_PRECISION=4
|
||||
export JSON_REAL_PRECISION
|
||||
1
test/suites/encoding-flags/real-precision/input
Normal file
1
test/suites/encoding-flags/real-precision/input
Normal file
@@ -0,0 +1 @@
|
||||
[1.23456789, 1.0, 1.0000000000000002]
|
||||
1
test/suites/encoding-flags/real-precision/output
Normal file
1
test/suites/encoding-flags/real-precision/output
Normal file
@@ -0,0 +1 @@
|
||||
[1.235, 1.0, 1.0]
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
1 33 33
|
||||
\u0000 is not allowed
|
||||
@@ -1 +0,0 @@
|
||||
["\u0000 (null byte not allowed)"]
|
||||
2
test/suites/invalid/invalid-unicode-escape/error
Normal file
2
test/suites/invalid/invalid-unicode-escape/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 5 5
|
||||
invalid escape near '"\uq'
|
||||
1
test/suites/invalid/invalid-unicode-escape/input
Normal file
1
test/suites/invalid/invalid-unicode-escape/input
Normal file
@@ -0,0 +1 @@
|
||||
["\uqqqq <-- invalid unicode escape"]
|
||||
2
test/suites/invalid/null-byte-in-object-key/error
Normal file
2
test/suites/invalid/null-byte-in-object-key/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 15 15
|
||||
NUL byte in object key not supported near '"foo\u0000bar"'
|
||||
1
test/suites/invalid/null-byte-in-object-key/input
Normal file
1
test/suites/invalid/null-byte-in-object-key/input
Normal file
@@ -0,0 +1 @@
|
||||
{"foo\u0000bar": 42}
|
||||
2
test/suites/invalid/null-escape-in-string/error
Normal file
2
test/suites/invalid/null-escape-in-string/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 33 33
|
||||
\u0000 is not allowed without JSON_ALLOW_NUL
|
||||
1
test/suites/invalid/null-escape-in-string/input
Normal file
1
test/suites/invalid/null-escape-in-string/input
Normal file
@@ -0,0 +1 @@
|
||||
["null escape \u0000 not allowed"]
|
||||
2
test/suites/invalid/recursion-depth/error
Normal file
2
test/suites/invalid/recursion-depth/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1 2049 2049
|
||||
maximum parsing depth reached near '['
|
||||
1
test/suites/invalid/recursion-depth/input
Normal file
1
test/suites/invalid/recursion-depth/input
Normal file
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
1
test/suites/valid/real-subnormal-number/input
Normal file
1
test/suites/valid/real-subnormal-number/input
Normal file
@@ -0,0 +1 @@
|
||||
[1.8011670033376514e-308]
|
||||
1
test/suites/valid/real-subnormal-number/output
Normal file
1
test/suites/valid/real-subnormal-number/output
Normal file
@@ -0,0 +1 @@
|
||||
[1.8011670033376514e-308]
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
# Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
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
|
||||
@@ -1,108 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,69 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,3 +0,0 @@
|
||||
<?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