parent
5026d26b85
commit
14f734f631
|
@ -19,6 +19,8 @@ install-sh
|
|||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
compile
|
||||
test-driver
|
||||
*.lo
|
||||
*.la
|
||||
stamp-h1
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,34 +1,244 @@
|
|||
Version 2.6 (in development)
|
||||
============================
|
||||
Version 2.9
|
||||
===========
|
||||
|
||||
Released XXXX-XX-XX
|
||||
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.
|
||||
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.
|
||||
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()`.
|
||||
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.
|
||||
enabled by using the ``JSON_ALLOW_NUL`` decoding flag (#141).
|
||||
|
||||
- Add `json_boolean_value()` as an alias for `json_is_true()`
|
||||
(#146).
|
||||
|
||||
- Add JSON_REAL_PRECISION encoding flag/macro for controlling real
|
||||
number precision (#178).
|
||||
|
||||
- Define the maximum indentation as JSON_MAX_INDENT (#191).
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Some malformed ``\uNNNN`` escapes could crash the decoder with an
|
||||
assertion failure.
|
||||
|
||||
- Avoid integer overflows with very long strings in UTF-8 decoder and
|
||||
hashtable.
|
||||
|
||||
- Check for *NULL* key in `json_object_get()` and
|
||||
`json_object_del()` (#151).
|
||||
|
||||
- Enhance hashtable seeding on Windows (#162).
|
||||
|
||||
- `json_unpack()`: Allow mixing JSON_STRICT with optional keys
|
||||
(#162, #163).
|
||||
|
||||
- Fix int/int32 mismatch (#142).
|
||||
|
||||
- Parse subnormal numbers correctly (#202).
|
||||
|
||||
* Build:
|
||||
|
||||
- Remove VS2010 build files. CMake should be used on Windows instead
|
||||
(#165).
|
||||
|
||||
- Fix CMake build flags for MinGW (#193).
|
||||
|
||||
- Add CMake config files for find_package. Rename config.h to
|
||||
jansson_private_config.h (#157, #159).
|
||||
|
||||
- Make Valgrind checks work with CMake (#160).
|
||||
|
||||
- Fix feature checks to use correct __ATOMIC flags.
|
||||
|
||||
- Fix CMake checks for uint16_t and uint8_t support (#177).
|
||||
|
||||
- Make Jansson build on SmartOS/Solaris (#171).
|
||||
|
||||
- Work around a GCC bug on Solaris (#175).
|
||||
|
||||
- Fix autoreconf on Debian (#182).
|
||||
|
||||
- Don't use GNU make specific export for global AM_CFLAGS (#203,
|
||||
#204).
|
||||
|
||||
- Fix building on Android using the supplied Android.mk (#166,
|
||||
#174).
|
||||
|
||||
- Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS (#200).
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Document JANSSON_BUILD_SHARED_LIBS CMake option (#187).
|
||||
|
||||
* Tests:
|
||||
|
||||
- Close file handles correctly (#198).
|
||||
|
||||
* Other changes:
|
||||
|
||||
- ``\uNNNN`` escapes are now encoded in upper case for better
|
||||
readability.
|
||||
|
||||
- Enable usage of AddressSanitizer (#180).
|
||||
|
||||
|
||||
Version 2.6
|
||||
===========
|
||||
|
||||
Released 2014-02-11
|
||||
|
||||
* Security:
|
||||
|
||||
- CVE-2013-6401: The hash function used by the hashtable
|
||||
implementation has been changed, and is automatically seeded with
|
||||
random data when the first JSON object is created. This prevents
|
||||
an attacker from causing large JSON objects with specially crafted
|
||||
keys perform poorly.
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_object_seed()`: Set the seed value of the hash function.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Include CMake specific files in the release tarball.
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Fix tutorial source to send a User-Agent header, which is now
|
||||
required by the GitHub API.
|
||||
|
||||
- Set all memory to zero in secure_free() example.
|
||||
|
||||
|
||||
Version 2.5
|
||||
===========
|
||||
|
|
|
@ -50,63 +50,110 @@ cmake_minimum_required (VERSION 2.8)
|
|||
# required for exports? cmake_minimum_required (VERSION 2.8.6)
|
||||
project (jansson C)
|
||||
|
||||
# Options
|
||||
option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF)
|
||||
option(USE_URANDOM "Use /dev/urandom to seed the hash function." ON)
|
||||
option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." ON)
|
||||
|
||||
if (MSVC)
|
||||
# This option must match the settings used in your program, in particular if you
|
||||
# are linking statically
|
||||
option(JANSSON_STATIC_CRT "Link the static CRT libraries" ON )
|
||||
endif ()
|
||||
|
||||
# Disabled by OBS
|
||||
option(JANSSON_EXAMPLES "Compile example applications" OFF)
|
||||
|
||||
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.5")
|
||||
set(JANSSON_DISPLAY_VERSION "2.9")
|
||||
|
||||
# This is what is required to match the same numbers as automake's
|
||||
set (JANSSON_VERSION "4.5.0")
|
||||
set (JANSSON_SOVERSION 4)
|
||||
set(JANSSON_VERSION "4.9.0")
|
||||
set(JANSSON_SOVERSION 4)
|
||||
|
||||
# for CheckFunctionKeywords
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include (CheckCSourceCompiles)
|
||||
include (CheckFunctionExists)
|
||||
include (CheckFunctionKeywords)
|
||||
include (CheckIncludeFiles)
|
||||
include (CheckTypeSize)
|
||||
include (CheckSymbolExists)
|
||||
|
||||
|
||||
if (MSVC)
|
||||
# Turn off Microsofts "security" warnings.
|
||||
add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" )
|
||||
|
||||
|
||||
# Disabled by OBS, options already set by top level CMakeLists
|
||||
if (FALSE)
|
||||
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()
|
||||
|
||||
|
||||
endif()
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
set(CMAKE_C_FLAGS "-fPIC")
|
||||
add_definitions("-fPIC")
|
||||
endif()
|
||||
|
||||
|
||||
message("C compiler: ${CMAKE_C_COMPILER_ID}")
|
||||
|
||||
# Coverage only works with GCC for a debug build.
|
||||
if (JANSSON_COVERALLS)
|
||||
set(JANSSON_COVERAGE ON)
|
||||
endif()
|
||||
|
||||
if (JANSSON_COVERAGE)
|
||||
include(CodeCoverage)
|
||||
include(Coveralls)
|
||||
|
||||
# This adds coverage arguments to gcc/clang.
|
||||
coveralls_turn_on_coverage()
|
||||
endif()
|
||||
|
||||
check_include_files (endian.h HAVE_ENDIAN_H)
|
||||
check_include_files (fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files (sched.h HAVE_SCHED_H)
|
||||
check_include_files (unistd.h HAVE_UNISTD_H)
|
||||
check_include_files (sys/param.h HAVE_SYS_PARAM_H)
|
||||
check_include_files (sys/stat.h HAVE_SYS_STAT_H)
|
||||
check_include_files (sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_files (sys/time.h HAVE_SYS_TYPES_H)
|
||||
|
||||
check_function_exists (close HAVE_CLOSE)
|
||||
check_function_exists (getpid HAVE_GETPID)
|
||||
check_function_exists (gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_function_exists (open HAVE_OPEN)
|
||||
check_function_exists (read HAVE_READ)
|
||||
check_function_exists (sched_yield HAVE_SCHED_YIELD)
|
||||
|
||||
# Check for the int-type includes
|
||||
check_include_files (sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_files (inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files (stdint.h HAVE_STDINT_H)
|
||||
|
||||
|
||||
# Check our 64 bit integer sizes
|
||||
check_type_size (__int64 __INT64)
|
||||
check_type_size (int64_t INT64_T)
|
||||
|
@ -117,17 +164,58 @@ check_type_size (int32_t INT32_T)
|
|||
check_type_size (__int32 __INT32)
|
||||
check_type_size ("long" LONG_INT)
|
||||
check_type_size ("int" INT)
|
||||
|
||||
if (HAVE_INT32_T)
|
||||
set (JSON_INT32 int32_t)
|
||||
elseif (HAVE___INT32)
|
||||
set (JSON_INT32 __int32)
|
||||
elseif (HAVE_LONG AND (${LONG_INT} EQUAL 4))
|
||||
elseif (HAVE_LONG_INT AND (${LONG_INT} EQUAL 4))
|
||||
set (JSON_INT32 long)
|
||||
elseif (HAVE_INT AND (${INT} EQUAL 4))
|
||||
set (JSON_INT32 int)
|
||||
else ()
|
||||
message (FATAL_ERROR "Could not detect a valid 32 bit integer type")
|
||||
message (FATAL_ERROR "Could not detect a valid 32-bit integer type")
|
||||
endif ()
|
||||
|
||||
check_type_size ("unsigned long" UNSIGNED_LONG_INT)
|
||||
check_type_size ("unsigned int" UNSIGNED_INT)
|
||||
check_type_size ("unsigned short" UNSIGNED_SHORT)
|
||||
|
||||
check_type_size (uint32_t UINT32_T)
|
||||
check_type_size (__uint32 __UINT32)
|
||||
if (HAVE_UINT32_T)
|
||||
set (JSON_UINT32 uint32_t)
|
||||
elseif (HAVE___UINT32)
|
||||
set (JSON_UINT32 __uint32)
|
||||
elseif (HAVE_UNSIGNED_LONG_INT AND (${UNSIGNED_LONG_INT} EQUAL 4))
|
||||
set (JSON_UINT32 "unsigned long")
|
||||
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 4))
|
||||
set (JSON_UINT32 "unsigned int")
|
||||
else ()
|
||||
message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type")
|
||||
endif ()
|
||||
|
||||
check_type_size (uint16_t UINT16_T)
|
||||
check_type_size (__uint16 __UINT16)
|
||||
if (HAVE_UINT16_T)
|
||||
set (JSON_UINT16 uint16_t)
|
||||
elseif (HAVE___UINT16)
|
||||
set (JSON_UINT16 __uint16)
|
||||
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 2))
|
||||
set (JSON_UINT16 "unsigned int")
|
||||
elseif (HAVE_UNSIGNED_SHORT AND (${UNSIGNED_SHORT} EQUAL 2))
|
||||
set (JSON_UINT16 "unsigned short")
|
||||
else ()
|
||||
message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type")
|
||||
endif ()
|
||||
|
||||
check_type_size (uint8_t UINT8_T)
|
||||
check_type_size (__uint8 __UINT8)
|
||||
if (HAVE_UINT8_T)
|
||||
set (JSON_UINT8 uint8_t)
|
||||
elseif (HAVE___UINT8)
|
||||
set (JSON_UINT8 __uint8)
|
||||
else ()
|
||||
set (JSON_UINT8 "unsigned char")
|
||||
endif ()
|
||||
|
||||
# Check for ssize_t and SSIZE_T existance.
|
||||
|
@ -166,7 +254,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)
|
||||
|
@ -197,12 +285,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.
|
||||
|
@ -211,38 +297,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)
|
||||
endif()
|
||||
|
||||
# Find our snprintf
|
||||
check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF)
|
||||
check_symbol_exists(_snprintf "stdio.h" HAVE__SNPRINTF)
|
||||
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS)
|
||||
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS)
|
||||
|
||||
if (HAVE_SNPRINTF)
|
||||
set (JSON_SNPRINTF snprintf)
|
||||
elseif (HAVE__SNPRINTF)
|
||||
set (JSON_SNPRINTF _snprintf)
|
||||
endif ()
|
||||
|
||||
# Create pkg-conf file.
|
||||
# (We use the same files as ./configure does, so we
|
||||
# have to defined the same variables used there).
|
||||
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR lib)
|
||||
endif(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(VERSION ${JANSSON_DISPLAY_VERSION})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
|
||||
set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.")
|
||||
|
||||
# configure the public config file
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
|
||||
|
@ -252,61 +320,67 @@ 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)
|
||||
|
||||
# Disabled by OBS, we use it as a static library
|
||||
if (FALSE)
|
||||
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)
|
||||
# Disabled by OBS
|
||||
if (FALSE)
|
||||
option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." OFF)
|
||||
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!
|
||||
|
@ -341,9 +415,9 @@ if (FALSE)
|
|||
# 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()
|
||||
|
@ -351,9 +425,9 @@ if (FALSE)
|
|||
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)
|
||||
|
@ -385,19 +459,23 @@ if (FALSE)
|
|||
endif()
|
||||
endif ()
|
||||
|
||||
# Disabled by OBS, we don't test this.
|
||||
if (FALSE)
|
||||
OPTION (TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
|
||||
# Disabled by OBS
|
||||
option(JANSSON_WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" ON)
|
||||
|
||||
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)
|
||||
|
||||
# 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}")
|
||||
|
@ -441,36 +519,175 @@ if (FALSE)
|
|||
# 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.
|
||||
#
|
||||
# Disabled by OBS
|
||||
option(JANSSON_INSTALL "Generate installation target" OFF)
|
||||
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")
|
||||
|
||||
target_include_directories(jansson
|
||||
PUBLIC src "${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
PUBLIC src "${CMAKE_CURRENT_BINARY_DIR}/include")
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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("###########################################################################")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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,45 +0,0 @@
|
|||
/* Reduced down to the defines that are actually used in the code */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> (and friends) header file. */
|
||||
#cmakedefine HAVE_INTTYPES_H 1
|
||||
#cmakedefine HAVE_STDINT_H 1
|
||||
#cmakedefine HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* We must include this here, as in (eg) utf.h it will want to use
|
||||
the integer type, which in MSVC2010 will be in stdint.h
|
||||
(there is no inttypes.h in MSVC2010) */
|
||||
#if defined(HAVE_STDINT_H)
|
||||
# include <stdint.h>
|
||||
#elif defined(HAVE_INTTYPES_H)
|
||||
# include <inttypes.h>
|
||||
#elif defined(HAVE_SYS_TYPES_H)
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
/* Define to 1 if you have the <locale.h> header file. */
|
||||
#cmakedefine HAVE_LOCALE_H 1
|
||||
|
||||
/* Define to 1 if you have the 'setlocale' function. */
|
||||
#cmakedefine HAVE_SETLOCALE 1
|
||||
|
||||
/* Define to the type of a signed integer type of width exactly 32 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
#cmakedefine HAVE_INT32_T 1
|
||||
|
||||
#ifndef HAVE_INT32_T
|
||||
# define int32_t @JSON_INT32@
|
||||
#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
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
#cmakedefine HAVE_ENDIAN_H 1
|
||||
#cmakedefine HAVE_FCNTL_H 1
|
||||
#cmakedefine HAVE_SCHED_H 1
|
||||
#cmakedefine HAVE_UNISTD_H 1
|
||||
#cmakedefine HAVE_SYS_PARAM_H 1
|
||||
#cmakedefine HAVE_SYS_STAT_H 1
|
||||
#cmakedefine HAVE_SYS_TIME_H 1
|
||||
#cmakedefine HAVE_SYS_TYPES_H 1
|
||||
#cmakedefine HAVE_STDINT_H 1
|
||||
|
||||
#cmakedefine HAVE_CLOSE 1
|
||||
#cmakedefine HAVE_GETPID 1
|
||||
#cmakedefine HAVE_GETTIMEOFDAY 1
|
||||
#cmakedefine HAVE_OPEN 1
|
||||
#cmakedefine HAVE_READ 1
|
||||
#cmakedefine HAVE_SCHED_YIELD 1
|
||||
|
||||
#cmakedefine HAVE_SYNC_BUILTINS 1
|
||||
#cmakedefine HAVE_ATOMIC_BUILTINS 1
|
||||
|
||||
#cmakedefine HAVE_LOCALE_H 1
|
||||
#cmakedefine HAVE_SETLOCALE 1
|
||||
|
||||
#cmakedefine HAVE_INT32_T 1
|
||||
#ifndef HAVE_INT32_T
|
||||
# define int32_t @JSON_INT32@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT32_T 1
|
||||
#ifndef HAVE_UINT32_T
|
||||
# define uint32_t @JSON_UINT32@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT16_T 1
|
||||
#ifndef HAVE_UINT16_T
|
||||
# define uint16_t @JSON_UINT16@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_UINT8_T 1
|
||||
#ifndef HAVE_UINT8_T
|
||||
# define uint8_t @JSON_UINT8@
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_SSIZE_T 1
|
||||
|
||||
#ifndef HAVE_SSIZE_T
|
||||
# define ssize_t @JSON_SSIZE@
|
||||
#endif
|
||||
|
||||
#cmakedefine USE_URANDOM 1
|
||||
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
|
||||
|
||||
#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@
|
|
@ -1,10 +1,11 @@
|
|||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.5], [petri@digip.org])
|
||||
AC_INIT([jansson], [2.9], [petri@digip.org])
|
||||
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/value.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_HEADERS([jansson_private_config.h])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
|
@ -14,10 +15,13 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
|
|||
# Checks for libraries.
|
||||
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([locale.h])
|
||||
AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/stat.h sys/time.h sys/types.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT8_T
|
||||
AC_TYPE_LONG_LONG_INT
|
||||
|
||||
AC_C_INLINE
|
||||
|
@ -29,7 +33,31 @@ esac
|
|||
AC_SUBST([json_inline])
|
||||
|
||||
# Checks for library functions.
|
||||
AC_CHECK_FUNCS([strtoll localeconv])
|
||||
AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll])
|
||||
|
||||
AC_MSG_CHECKING([for gcc __sync builtins])
|
||||
have_sync_builtins=no
|
||||
AC_TRY_LINK(
|
||||
[], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1);],
|
||||
[have_sync_builtins=yes],
|
||||
)
|
||||
if test "x$have_sync_builtins" = "xyes"; then
|
||||
AC_DEFINE([HAVE_SYNC_BUILTINS], [1],
|
||||
[Define to 1 if gcc's __sync builtins are available])
|
||||
fi
|
||||
AC_MSG_RESULT([$have_sync_builtins])
|
||||
|
||||
AC_MSG_CHECKING([for gcc __atomic builtins])
|
||||
have_atomic_builtins=no
|
||||
AC_TRY_LINK(
|
||||
[], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE);],
|
||||
[have_atomic_builtins=yes],
|
||||
)
|
||||
if test "x$have_atomic_builtins" = "xyes"; then
|
||||
AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1],
|
||||
[Define to 1 if gcc's __atomic builtins are available])
|
||||
fi
|
||||
AC_MSG_RESULT([$have_atomic_builtins])
|
||||
|
||||
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
|
||||
yesyes) json_have_long_long=1;;
|
||||
|
@ -43,6 +71,39 @@ case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
|
|||
esac
|
||||
AC_SUBST([json_have_localeconv])
|
||||
|
||||
# Features
|
||||
AC_ARG_ENABLE([urandom],
|
||||
[AS_HELP_STRING([--disable-urandom],
|
||||
[Don't use /dev/urandom to seed the hash function])],
|
||||
[use_urandom=$enableval], [use_urandom=yes])
|
||||
|
||||
if test "x$use_urandom" = xyes; then
|
||||
AC_DEFINE([USE_URANDOM], [1],
|
||||
[Define to 1 if /dev/urandom should be used for seeding the hash function])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([windows-cryptoapi],
|
||||
[AS_HELP_STRING([--disable-windows-cryptoapi],
|
||||
[Don't use CryptGenRandom to seed the hash function])],
|
||||
[use_windows_cryptoapi=$enableval], [use_windows_cryptoapi=yes])
|
||||
|
||||
if test "x$use_windows_cryptoapi" = xyes; then
|
||||
AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1],
|
||||
[Define to 1 if CryptGenRandom should be used for seeding the hash function])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([initial-hashtable-order],
|
||||
[AS_HELP_STRING([--enable-initial-hashtable-order=VAL],
|
||||
[Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.])],
|
||||
[initial_hashtable_order=$enableval], [initial_hashtable_order=3])
|
||||
AC_DEFINE_UNQUOTED([INITIAL_HASHTABLE_ORDER], [$initial_hashtable_order],
|
||||
[Number of buckets new object hashtables contain is 2 raised to this power. E.g. 3 -> 2^3 = 8.])
|
||||
|
||||
if test x$GCC = xyes; then
|
||||
AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement"
|
||||
fi
|
||||
AC_SUBST([AM_CFLAGS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
jansson.pc
|
||||
Makefile
|
||||
|
|
|
@ -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:
|
||||
|
@ -155,6 +152,8 @@ functions:
|
|||
Alias of :func:`json_is_true()`, i.e. returns 1 for ``JSON_TRUE``
|
||||
and 0 otherwise.
|
||||
|
||||
.. versionadded:: 2.7
|
||||
|
||||
|
||||
.. _apiref-reference-count:
|
||||
|
||||
|
@ -169,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*.
|
||||
|
@ -256,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
|
||||
====================
|
||||
|
@ -297,18 +314,16 @@ 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+0000 through
|
||||
U+10FFFF are allowed, but you must use length-aware functions if you
|
||||
wish to embed NUL bytes in strings.
|
||||
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)
|
||||
|
||||
|
@ -337,7 +352,7 @@ wish to embed NUL bytes in strings.
|
|||
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.
|
||||
|
||||
|
@ -346,7 +361,7 @@ wish to embed NUL bytes in strings.
|
|||
Returns the length of *string* in its UTF-8 presentation, or zero
|
||||
if *string* is not a JSON string.
|
||||
|
||||
.. function:: int json_string_set(const json_t *string, const char *value)
|
||||
.. 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
|
||||
|
@ -357,7 +372,7 @@ wish to embed NUL bytes in strings.
|
|||
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(const json_t *string, const char *value)
|
||||
.. 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
|
||||
|
@ -407,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::
|
||||
|
@ -448,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
|
||||
|
@ -530,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)
|
||||
|
@ -538,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
|
||||
|
@ -562,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
|
||||
|
||||
|
@ -574,7 +582,7 @@ 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 NUL bytes are allowed in string values, they are not
|
||||
Even though null bytes are allowed in string values, they are not
|
||||
allowed in object keys.
|
||||
|
||||
.. function:: json_t *json_object(void)
|
||||
|
@ -656,9 +664,6 @@ allowed in object keys.
|
|||
|
||||
.. 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
|
||||
|
@ -674,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)
|
||||
|
||||
|
@ -736,24 +754,48 @@ 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::
|
||||
.. function:: void json_object_seed(size_t seed)
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
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.
|
||||
|
||||
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);
|
||||
}
|
||||
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)
|
||||
and the process ID.
|
||||
|
||||
If called at all, this function must be called before any calls to
|
||||
:func:`json_object()`, either explicit or implicit. If this
|
||||
function is not called by the user, the first call to
|
||||
:func:`json_object()` (either explicit or implicit) seeds the hash
|
||||
function. See :ref:`portability-thread-safety` for notes on thread
|
||||
safety.
|
||||
|
||||
If repeatable results are required, for e.g. unit tests, the hash
|
||||
function can be "unrandomized" by calling :func:`json_object_seed`
|
||||
with a constant value on program startup, e.g.
|
||||
``json_object_seed(1)``.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
|
||||
Error reporting
|
||||
|
@ -774,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
|
||||
|
||||
|
@ -786,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.
|
||||
|
@ -840,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
|
||||
|
@ -848,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``
|
||||
|
@ -857,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
|
||||
|
@ -879,18 +930,27 @@ 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
|
||||
|
||||
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:: int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
|
||||
Write the JSON representation of *root* to the stream *output*.
|
||||
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
|
||||
|
@ -898,7 +958,7 @@ is in UTF-8.
|
|||
|
||||
.. 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.
|
||||
|
||||
|
@ -921,7 +981,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
|
||||
|
@ -951,7 +1011,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.
|
||||
|
||||
|
@ -962,8 +1022,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
|
||||
|
@ -998,13 +1058,13 @@ macros can be ORed together to obtain *flags*.
|
|||
|
||||
``JSON_ALLOW_NUL``
|
||||
Allow ``\u0000`` escape inside string values. This is a safety
|
||||
measure; If you know your input can contain NUL bytes, use this
|
||||
flag. If you don't use this flag, you don't have to worry about NUL
|
||||
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 NUL bytes even if this flag is
|
||||
Object keys cannot have embedded null bytes even if this flag is
|
||||
used.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
@ -1021,8 +1081,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
|
||||
|
@ -1128,7 +1186,13 @@ 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.
|
||||
|
@ -1184,6 +1248,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
|
||||
|
@ -1199,8 +1269,6 @@ arguments.
|
|||
|
||||
Whitespace, ``:`` and ``,`` are ignored.
|
||||
|
||||
The following functions compose the value building API:
|
||||
|
||||
.. function:: json_t *json_pack(const char *fmt, ...)
|
||||
|
||||
.. refcounting:: new
|
||||
|
@ -1237,11 +1305,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");
|
||||
|
||||
|
||||
|
@ -1266,13 +1334,13 @@ 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
|
||||
``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
|
||||
|
@ -1305,7 +1373,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
|
||||
|
@ -1317,7 +1385,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
|
||||
|
@ -1340,8 +1408,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
|
||||
|
@ -1407,7 +1473,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);
|
||||
|
@ -1443,13 +1509,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*.
|
||||
|
||||
|
||||
|
@ -1468,6 +1531,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
|
||||
|
@ -1511,6 +1576,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
|
||||
|
@ -1523,7 +1595,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.5'
|
||||
version = '2.9'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
|
|
@ -108,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.
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
Portability
|
||||
***********
|
||||
|
||||
.. _portability-thread-safety:
|
||||
|
||||
Thread safety
|
||||
-------------
|
||||
|
||||
Jansson is thread safe and has no mutable global state. The only
|
||||
exception are the memory allocation functions, that should be set at
|
||||
most once, and only on program startup. See
|
||||
:ref:`apiref-custom-memory-allocation`.
|
||||
exceptions are the hash function seed and memory allocation functions,
|
||||
see below.
|
||||
|
||||
There's no locking performed inside Jansson's code, so a multithreaded
|
||||
program must perform its own locking if JSON values are shared by
|
||||
|
@ -30,6 +31,36 @@ contained values with another array or object.
|
|||
If you want to make sure that two JSON value hierarchies do not
|
||||
contain shared values, use :func:`json_deep_copy()` to make copies.
|
||||
|
||||
|
||||
Hash function seed
|
||||
==================
|
||||
|
||||
To prevent an attacker from intentionally causing large JSON objects
|
||||
with specially crafted keys to perform very slow, the hash function
|
||||
used by Jansson is randomized using a seed value. The seed is
|
||||
automatically generated on the first explicit or implicit call to
|
||||
:func:`json_object()`, if :func:`json_object_seed()` has not been
|
||||
called beforehand.
|
||||
|
||||
The seed is generated by using operating system's entropy sources if
|
||||
they are available (``/dev/urandom``, ``CryptGenRandom()``). The
|
||||
initialization is done in as thread safe manner as possible, by using
|
||||
architecture specific lockless operations if provided by the platform
|
||||
or the compiler.
|
||||
|
||||
If you're using threads, it's recommended to autoseed the hashtable
|
||||
explicitly before spawning any threads by calling
|
||||
``json_object_seed(0)`` , especially if you're unsure whether the
|
||||
initialization is thread safe on your platform.
|
||||
|
||||
|
||||
Memory allocation functions
|
||||
===========================
|
||||
|
||||
Memory allocation functions should be set at most once, and only on
|
||||
program startup. See :ref:`apiref-custom-memory-allocation`.
|
||||
|
||||
|
||||
Locale
|
||||
------
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Jansson examples
|
||||
================
|
||||
|
||||
This directory contains simple example programs that use Jansson.
|
|
@ -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 = \
|
||||
|
@ -8,8 +9,10 @@ libjansson_la_SOURCES = \
|
|||
error.c \
|
||||
hashtable.c \
|
||||
hashtable.h \
|
||||
hashtable_seed.c \
|
||||
jansson_private.h \
|
||||
load.c \
|
||||
lookup3.h \
|
||||
memory.c \
|
||||
pack_unpack.c \
|
||||
strbuffer.c \
|
||||
|
@ -21,4 +24,4 @@ libjansson_la_SOURCES = \
|
|||
libjansson_la_LDFLAGS = \
|
||||
-no-undefined \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 9:0:5
|
||||
-version-info 13:0:9
|
||||
|
|
|
@ -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.
|
||||
|
@ -22,10 +22,8 @@
|
|||
#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)
|
||||
|
||||
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
|
@ -45,17 +43,21 @@ 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))
|
||||
|
@ -127,7 +129,7 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
|
|||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04X", codepoint);
|
||||
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
|
@ -140,7 +142,7 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -158,18 +160,9 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
|
|||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
static int object_key_compare_keys(const void *key1, const void *key2)
|
||||
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,
|
||||
|
@ -208,7 +201,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;
|
||||
|
||||
|
@ -220,8 +214,9 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
size_t n;
|
||||
size_t i;
|
||||
|
||||
json_array_t *array;
|
||||
|
||||
/* detect circular references */
|
||||
|
@ -230,7 +225,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||
goto array_error;
|
||||
array->visited = 1;
|
||||
|
||||
n = (int)json_array_size(json);
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
goto array_error;
|
||||
|
@ -300,40 +295,33 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||
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;
|
||||
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] = (char *)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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,41 @@
|
|||
/*
|
||||
* 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 <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <jansson_config.h> /* for JSON_INLINE */
|
||||
#include "jansson_private.h" /* for container_of() */
|
||||
#include "hashtable.h"
|
||||
|
||||
#ifndef INITIAL_HASHTABLE_ORDER
|
||||
#define INITIAL_HASHTABLE_ORDER 3
|
||||
#endif
|
||||
|
||||
typedef struct hashtable_list list_t;
|
||||
typedef struct hashtable_pair pair_t;
|
||||
typedef struct hashtable_bucket bucket_t;
|
||||
|
||||
extern volatile uint32_t hashtable_seed;
|
||||
|
||||
/* Implementation of the hash function */
|
||||
#include "lookup3.h"
|
||||
|
||||
#define list_to_pair(list_) container_of(list_, pair_t, list)
|
||||
|
||||
/* From http://www.cse.yorku.ca/~oz/hash.html */
|
||||
static size_t hash_str(const void *ptr)
|
||||
{
|
||||
const char *str = (const char *)ptr;
|
||||
|
||||
size_t hash = 5381;
|
||||
size_t c;
|
||||
|
||||
while((c = (size_t)*str))
|
||||
{
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
str++;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
|
||||
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
|
||||
|
||||
static JSON_INLINE void list_init(list_t *list)
|
||||
{
|
||||
|
@ -74,19 +77,6 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
|
|||
}
|
||||
}
|
||||
|
||||
static const size_t primes[] = {
|
||||
5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
|
||||
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
|
||||
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
|
||||
805306457, 1610612741
|
||||
};
|
||||
|
||||
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
|
||||
{
|
||||
return primes[hashtable->num_buckets];
|
||||
}
|
||||
|
||||
|
||||
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
const char *key, size_t hash)
|
||||
{
|
||||
|
@ -120,7 +110,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
|||
bucket_t *bucket;
|
||||
size_t index;
|
||||
|
||||
index = hash % num_buckets(hashtable);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
|
@ -137,6 +127,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
|||
bucket->last = pair->list.prev;
|
||||
|
||||
list_remove(&pair->list);
|
||||
list_remove(&pair->ordered_list);
|
||||
json_decref(pair->value);
|
||||
|
||||
jsonp_free(pair);
|
||||
|
@ -163,18 +154,21 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
|||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
size_t i, index, new_size;
|
||||
size_t i, index, new_size, new_order;
|
||||
struct hashtable_bucket *new_buckets;
|
||||
|
||||
jsonp_free(hashtable->buckets);
|
||||
new_order = hashtable->order + 1;
|
||||
new_size = hashsize(new_order);
|
||||
|
||||
hashtable->num_buckets++;
|
||||
new_size = num_buckets(hashtable);
|
||||
|
||||
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!new_buckets)
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
jsonp_free(hashtable->buckets);
|
||||
hashtable->buckets = new_buckets;
|
||||
hashtable->order = new_order;
|
||||
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
|
@ -199,14 +193,15 @@ int hashtable_init(hashtable_t *hashtable)
|
|||
size_t i;
|
||||
|
||||
hashtable->size = 0;
|
||||
hashtable->num_buckets = 0; /* index to primes[] */
|
||||
hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
|
||||
hashtable->order = INITIAL_HASHTABLE_ORDER;
|
||||
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
list_init(&hashtable->list);
|
||||
list_init(&hashtable->ordered_list);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
|
@ -221,21 +216,19 @@ void hashtable_close(hashtable_t *hashtable)
|
|||
jsonp_free(hashtable->buckets);
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value)
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
|
||||
{
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
size_t hash, index;
|
||||
|
||||
/* rehash if the load ratio exceeds 1 */
|
||||
if(hashtable->size >= num_buckets(hashtable))
|
||||
if(hashtable->size >= hashsize(hashtable->order))
|
||||
if(hashtable_do_rehash(hashtable))
|
||||
return -1;
|
||||
|
||||
hash = hash_str(key);
|
||||
index = hash % num_buckets(hashtable);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
|
||||
|
@ -261,12 +254,13 @@ int hashtable_set(hashtable_t *hashtable,
|
|||
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++;
|
||||
}
|
||||
|
@ -280,7 +274,7 @@ void *hashtable_get(hashtable_t *hashtable, const char *key)
|
|||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
|
@ -301,19 +295,20 @@ void hashtable_clear(hashtable_t *hashtable)
|
|||
|
||||
hashtable_do_clear(hashtable);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
}
|
||||
|
||||
list_init(&hashtable->list);
|
||||
list_init(&hashtable->ordered_list);
|
||||
hashtable->size = 0;
|
||||
}
|
||||
|
||||
void *hashtable_iter(hashtable_t *hashtable)
|
||||
{
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
|
@ -323,44 +318,38 @@ void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
|||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
return NULL;
|
||||
|
||||
return &pair->list;
|
||||
return &pair->ordered_list;
|
||||
}
|
||||
|
||||
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
|
||||
{
|
||||
list_t *list = (list_t *)iter;
|
||||
if(list->next == &hashtable->list)
|
||||
if(list->next == &hashtable->ordered_list)
|
||||
return NULL;
|
||||
return list->next;
|
||||
}
|
||||
|
||||
void *hashtable_iter_key(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->key;
|
||||
}
|
||||
|
||||
size_t hashtable_iter_serial(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
return pair->serial;
|
||||
}
|
||||
|
||||
void *hashtable_iter_value(void *iter)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void hashtable_iter_set(void *iter, json_t *value)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
|
||||
json_decref(pair->value);
|
||||
pair->value = value;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2009-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];
|
||||
};
|
||||
|
||||
|
@ -32,13 +35,15 @@ struct hashtable_bucket {
|
|||
typedef struct hashtable {
|
||||
size_t size;
|
||||
struct hashtable_bucket *buckets;
|
||||
size_t num_buckets; /* index to primes[] */
|
||||
size_t order; /* hashtable has pow(2, order) buckets */
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
} hashtable_t;
|
||||
|
||||
|
||||
#define hashtable_key_to_iter(key_) \
|
||||
(&(container_of(key_, struct hashtable_pair, key)->list))
|
||||
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
|
||||
|
||||
|
||||
/**
|
||||
* hashtable_init - Initialize a hashtable object
|
||||
|
@ -76,9 +81,7 @@ void hashtable_close(hashtable_t *hashtable);
|
|||
*
|
||||
* Returns 0 on success, -1 on failure (out of memory).
|
||||
*/
|
||||
int hashtable_set(hashtable_t *hashtable,
|
||||
const char *key, size_t serial,
|
||||
json_t *value);
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
|
||||
|
||||
/**
|
||||
* hashtable_get - Get a value associated with a key
|
||||
|
@ -155,13 +158,6 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
|
|||
*/
|
||||
void *hashtable_iter_key(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_serial - Retrieve the serial number pointed to by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
*/
|
||||
size_t hashtable_iter_serial(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_value - Retrieve the value pointed by an iterator
|
||||
*
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
|
||||
the hash function.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SCHED_H
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
|
||||
|
||||
static uint32_t buf_to_uint32(char *data) {
|
||||
size_t i;
|
||||
uint32_t result = 0;
|
||||
|
||||
for (i = 0; i < sizeof(uint32_t); i++)
|
||||
result = (result << 8) | (unsigned char)data[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* /dev/urandom */
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
static int seed_from_urandom(uint32_t *seed) {
|
||||
/* Use unbuffered I/O if we have open(), close() and read(). Otherwise
|
||||
fall back to fopen() */
|
||||
|
||||
char data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ)
|
||||
int urandom;
|
||||
urandom = open("/dev/urandom", O_RDONLY);
|
||||
if (urandom == -1)
|
||||
return 1;
|
||||
|
||||
ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t);
|
||||
close(urandom);
|
||||
#else
|
||||
FILE *urandom;
|
||||
|
||||
urandom = fopen("/dev/urandom", "rb");
|
||||
if (!urandom)
|
||||
return 1;
|
||||
|
||||
ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t);
|
||||
fclose(urandom);
|
||||
#endif
|
||||
|
||||
if (!ok)
|
||||
return 1;
|
||||
|
||||
*seed = buf_to_uint32(data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Windows Crypto API */
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
#include <wincrypt.h>
|
||||
|
||||
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags);
|
||||
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
|
||||
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
|
||||
|
||||
static int seed_from_windows_cryptoapi(uint32_t *seed)
|
||||
{
|
||||
HINSTANCE hAdvAPI32 = NULL;
|
||||
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
|
||||
CRYPTGENRANDOM pCryptGenRandom = NULL;
|
||||
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
|
||||
HCRYPTPROV hCryptProv = 0;
|
||||
BYTE data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
|
||||
if(hAdvAPI32 == NULL)
|
||||
return 1;
|
||||
|
||||
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
|
||||
if (!pCryptAcquireContext)
|
||||
return 1;
|
||||
|
||||
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom");
|
||||
if (!pCryptGenRandom)
|
||||
return 1;
|
||||
|
||||
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
|
||||
if (!pCryptReleaseContext)
|
||||
return 1;
|
||||
|
||||
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||
return 1;
|
||||
|
||||
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
|
||||
pCryptReleaseContext(hCryptProv, 0);
|
||||
|
||||
if (!ok)
|
||||
return 1;
|
||||
|
||||
*seed = buf_to_uint32((char *)data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* gettimeofday() and getpid() */
|
||||
static int seed_from_timestamp_and_pid(uint32_t *seed) {
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
/* XOR of seconds and microseconds */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
*seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec;
|
||||
#else
|
||||
/* Seconds only */
|
||||
*seed = (uint32_t)time(NULL);
|
||||
#endif
|
||||
|
||||
/* XOR with PID for more randomness */
|
||||
#if defined(_WIN32)
|
||||
*seed ^= (uint32_t)GetCurrentProcessId();
|
||||
#elif defined(HAVE_GETPID)
|
||||
*seed ^= (uint32_t)getpid();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t generate_seed() {
|
||||
uint32_t seed;
|
||||
int done = 0;
|
||||
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
if (seed_from_urandom(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
if (seed_from_windows_cryptoapi(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
if (!done) {
|
||||
/* Fall back to timestamp and PID if no better randomness is
|
||||
available */
|
||||
seed_from_timestamp_and_pid(&seed);
|
||||
}
|
||||
|
||||
/* Make sure the seed is never zero */
|
||||
if (seed == 0)
|
||||
seed = 1;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
volatile uint32_t hashtable_seed = 0;
|
||||
|
||||
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||
static volatile char seed_initialized = 0;
|
||||
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) {
|
||||
/* Do the seeding ourselves */
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
__atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE);
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
do {
|
||||
#ifdef HAVE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#endif
|
||||
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (new_seed == 0) {
|
||||
/* Explicit synchronization fences are not supported by the
|
||||
__sync builtins, so every thread getting here has to
|
||||
generate the seed value.
|
||||
*/
|
||||
new_seed = generate_seed();
|
||||
}
|
||||
|
||||
do {
|
||||
if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) {
|
||||
/* We were the first to seed */
|
||||
break;
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
#ifdef HAVE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
} while(hashtable_seed == 0);
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
static long seed_initialized = 0;
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (InterlockedIncrement(&seed_initialized) == 1) {
|
||||
/* Do the seeding ourselves */
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
hashtable_seed = new_seed;
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
do {
|
||||
SwitchToThread();
|
||||
} while (hashtable_seed == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Fall back to a thread-unsafe version */
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
hashtable_seed = new_seed;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -46,6 +46,7 @@ EXPORTS
|
|||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_object_key_to_iter
|
||||
json_object_seed
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
|
@ -65,4 +66,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 5
|
||||
#define JANSSON_MINOR_VERSION 9
|
||||
#define JANSSON_MICRO_VERSION 0
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.5"
|
||||
#define JANSSON_VERSION "2.9"
|
||||
|
||||
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
|
||||
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
|
||||
|
@ -112,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 */
|
||||
|
||||
|
@ -129,6 +142,7 @@ typedef struct {
|
|||
|
||||
/* getters, setters, manipulation */
|
||||
|
||||
void json_object_seed(size_t seed);
|
||||
size_t json_object_size(const json_t *object);
|
||||
json_t *json_object_get(const json_t *object, const char *key);
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
|
@ -151,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)); \
|
||||
|
@ -258,13 +279,15 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla
|
|||
|
||||
/* 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)
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
|
@ -279,6 +302,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.
|
||||
|
@ -34,7 +34,6 @@
|
|||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
size_t serial;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
|
@ -81,7 +80,7 @@ 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);
|
||||
|
@ -90,10 +89,20 @@ 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
|
||||
|
|
|
@ -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.
|
||||
|
@ -61,6 +61,8 @@ typedef struct {
|
|||
typedef struct {
|
||||
stream_t stream;
|
||||
strbuffer_t saved_text;
|
||||
size_t flags;
|
||||
size_t depth;
|
||||
int token;
|
||||
union {
|
||||
struct {
|
||||
|
@ -169,9 +171,9 @@ 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 = (int)utf8_check_first(c);
|
||||
count = utf8_check_first(c);
|
||||
if(!count)
|
||||
goto out;
|
||||
|
||||
|
@ -265,7 +267,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);
|
||||
|
@ -338,7 +340,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;
|
||||
|
@ -483,7 +485,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;
|
||||
|
||||
|
@ -498,26 +500,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");
|
||||
|
@ -527,7 +531,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;
|
||||
}
|
||||
|
||||
|
@ -539,9 +543,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') {
|
||||
|
@ -554,20 +558,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:
|
||||
|
@ -583,9 +587,9 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
|||
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;
|
||||
|
@ -614,9 +618,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);
|
||||
|
@ -654,12 +658,13 @@ static char *lex_steal_string(lex_t *lex, size_t *out_len)
|
|||
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;
|
||||
}
|
||||
|
@ -798,7 +803,12 @@ 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: {
|
||||
|
@ -821,15 +831,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -870,6 +872,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
|||
if(!json)
|
||||
return NULL;
|
||||
|
||||
lex->depth--;
|
||||
return json;
|
||||
}
|
||||
|
||||
|
@ -877,6 +880,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 != '{') {
|
||||
|
@ -909,7 +914,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)
|
||||
|
@ -942,7 +947,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);
|
||||
|
@ -987,7 +992,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);
|
||||
|
@ -1014,7 +1019,7 @@ 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);
|
||||
|
@ -1095,7 +1100,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);
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
|
||||
These are functions for producing 32-bit hashes for hash table lookup.
|
||||
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
are externally useful functions. Routines to test the hash are included
|
||||
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
the public domain. It has no warranty.
|
||||
|
||||
You probably want to use hashlittle(). hashlittle() and hashbig()
|
||||
hash byte arrays. hashlittle() is is faster than hashbig() on
|
||||
little-endian machines. Intel and AMD are little-endian machines.
|
||||
On second thought, you probably want hashlittle2(), which is identical to
|
||||
hashlittle() except it returns two 32-bit hashes for the price of one.
|
||||
You could implement hashbig2() if you wanted but I haven't bothered here.
|
||||
|
||||
If you want to find a hash of, say, exactly 7 integers, do
|
||||
a = i1; b = i2; c = i3;
|
||||
mix(a,b,c);
|
||||
a += i4; b += i5; c += i6;
|
||||
mix(a,b,c);
|
||||
a += i7;
|
||||
final(a,b,c);
|
||||
then use c as the hash value. If you have a variable length array of
|
||||
4-byte integers to hash, use hashword(). If you have a byte array (like
|
||||
a character string), use hashlittle(). If you have several byte arrays, or
|
||||
a mix of things, see the comments above hashlittle().
|
||||
|
||||
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||
then mix those integers. This is fast (you can do a lot more thorough
|
||||
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h> /* defines uint32_t etc */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h> /* attempt to define endianness */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
# include <endian.h> /* attempt to define endianness */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* My best guess at if you are big-endian or little-endian. This may
|
||||
* need adjustment.
|
||||
*/
|
||||
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
|
||||
__BYTE_ORDER == __LITTLE_ENDIAN) || \
|
||||
(defined(i386) || defined(__i386__) || defined(__i486__) || \
|
||||
defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
|
||||
# define HASH_LITTLE_ENDIAN 1
|
||||
# define HASH_BIG_ENDIAN 0
|
||||
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
|
||||
__BYTE_ORDER == __BIG_ENDIAN) || \
|
||||
(defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
|
||||
# define HASH_LITTLE_ENDIAN 0
|
||||
# define HASH_BIG_ENDIAN 1
|
||||
#else
|
||||
# define HASH_LITTLE_ENDIAN 0
|
||||
# define HASH_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#define hashsize(n) ((size_t)1<<(n))
|
||||
#define hashmask(n) (hashsize(n)-1)
|
||||
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
|
||||
This is reversible, so any information in (a,b,c) before mix() is
|
||||
still in (a,b,c) after mix().
|
||||
|
||||
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||
mix() in reverse, there are at least 32 bits of the output that
|
||||
are sometimes the same for one pair and different for another pair.
|
||||
This was tested for:
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||
satisfy this are
|
||||
4 6 8 16 19 4
|
||||
9 15 3 18 27 15
|
||||
14 9 3 7 17 3
|
||||
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
the operations, constants, and arrangements of the variables.
|
||||
|
||||
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
avalanche in c.
|
||||
|
||||
This allows some parallelism. Read-after-writes are good at doubling
|
||||
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
direction as the goal of parallelism. I did what I could. Rotates
|
||||
seem to cost as much as shifts on every machine I could lay my hands
|
||||
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
rotates.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= rot(c, 4); c += b; \
|
||||
b -= a; b ^= rot(a, 6); a += c; \
|
||||
c -= b; c ^= rot(b, 8); b += a; \
|
||||
a -= c; a ^= rot(c,16); c += b; \
|
||||
b -= a; b ^= rot(a,19); a += c; \
|
||||
c -= b; c ^= rot(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
|
||||
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
produce values of c that look totally different. This was tested for
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
These constants passed:
|
||||
14 11 25 16 4 14 24
|
||||
12 14 25 16 4 14 24
|
||||
and these came close:
|
||||
4 8 15 26 3 22 24
|
||||
10 8 15 26 3 22 24
|
||||
11 8 15 26 3 22 24
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define final(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= rot(b,14); \
|
||||
a ^= c; a -= rot(c,11); \
|
||||
b ^= a; b -= rot(a,25); \
|
||||
c ^= b; c -= rot(b,16); \
|
||||
a ^= c; a -= rot(c,4); \
|
||||
b ^= a; b -= rot(a,14); \
|
||||
c ^= b; c -= rot(b,24); \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
hashlittle() -- hash a variable-length key into a 32-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
length : the length of the key, counting by bytes
|
||||
initval : can be any 4-byte value
|
||||
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
the return value. Two keys differing by one or two bits will have
|
||||
totally different hash values.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (uint8_t **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
|
||||
/* Detect Valgrind or AddressSanitizer */
|
||||
#ifdef VALGRIND
|
||||
# define NO_MASKING_TRICK 1
|
||||
#else
|
||||
# if defined(__has_feature) /* Clang */
|
||||
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# else
|
||||
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NO_MASKING_TRICK
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef NO_MASKING_TRICK
|
||||
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
|
||||
case 5 : b+=k[1]&0xff; a+=k[0]; break;
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=k[0]&0xffffff; break;
|
||||
case 2 : a+=k[0]&0xffff; break;
|
||||
case 1 : a+=k[0]&0xff; break;
|
||||
case 0 : return c; /* zero length strings require no mixing */
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return c;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*--------------- all but last block: aligned reads and different mixing */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=k[4];
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=k[2];
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=k[0];
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return c; /* zero length requires no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
case 12: c+=((uint32_t)k[11])<<24;
|
||||
case 11: c+=((uint32_t)k[10])<<16;
|
||||
case 10: c+=((uint32_t)k[9])<<8;
|
||||
case 9 : c+=k[8];
|
||||
case 8 : b+=((uint32_t)k[7])<<24;
|
||||
case 7 : b+=((uint32_t)k[6])<<16;
|
||||
case 6 : b+=((uint32_t)k[5])<<8;
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((uint32_t)k[3])<<24;
|
||||
case 3 : a+=((uint32_t)k[2])<<16;
|
||||
case 2 : a+=((uint32_t)k[1])<<8;
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return c;
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return c;
|
||||
}
|
|
@ -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
|
||||
|
@ -59,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)
|
||||
{
|
||||
|
@ -240,10 +239,10 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
|||
}
|
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -291,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)) {
|
||||
|
@ -301,20 +322,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
|||
return pack_array(s, ap);
|
||||
|
||||
case 's': /* string */
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int ours;
|
||||
|
||||
str = read_string(s, ap, "string", &len, &ours);
|
||||
if(!str)
|
||||
return NULL;
|
||||
|
||||
if (ours)
|
||||
return jsonp_stringn_nocheck_own(str, len);
|
||||
else
|
||||
return json_stringn_nocheck(str, len);
|
||||
}
|
||||
return pack_string(s, ap);
|
||||
|
||||
case 'n': /* null */
|
||||
return json_null();
|
||||
|
@ -332,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'",
|
||||
|
@ -350,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
|
||||
|
@ -406,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);
|
||||
}
|
||||
|
||||
|
@ -425,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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -9,24 +9,12 @@
|
|||
#define UTF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
/* inttypes.h includes stdint.h in a standard environment, so there's
|
||||
no need to include stdint.h separately. If inttypes.h doesn't define
|
||||
int32_t, it's defined in config.h. */
|
||||
#include <inttypes.h>
|
||||
#endif /* HAVE_INTTYPES_H */
|
||||
|
||||
#else /* !HAVE_CONFIG_H */
|
||||
#ifdef _WIN32
|
||||
typedef int int32_t;
|
||||
#else /* !_WIN32 */
|
||||
/* Assume a standard environment */
|
||||
#include <inttypes.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
|
||||
|
||||
|
|
|
@ -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,11 +9,19 @@
|
|||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
#include "jansson_private.h"
|
||||
|
@ -21,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
|
||||
|
@ -36,11 +46,19 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
|||
|
||||
/*** object ***/
|
||||
|
||||
extern volatile uint32_t hashtable_seed;
|
||||
|
||||
json_t *json_object(void)
|
||||
{
|
||||
json_object_t *object = jsonp_malloc(sizeof(json_object_t));
|
||||
if(!object)
|
||||
return NULL;
|
||||
|
||||
if (!hashtable_seed) {
|
||||
/* Autoseed */
|
||||
json_object_seed(0);
|
||||
}
|
||||
|
||||
json_init(&object->json, JSON_OBJECT);
|
||||
|
||||
if(hashtable_init(&object->hashtable))
|
||||
|
@ -49,7 +67,6 @@ json_t *json_object(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
object->serial = 0;
|
||||
object->visited = 0;
|
||||
|
||||
return &object->json;
|
||||
|
@ -97,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;
|
||||
|
@ -136,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;
|
||||
}
|
||||
|
@ -913,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 */
|
||||
}
|
||||
|
@ -946,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -972,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;
|
||||
}
|
||||
|
@ -998,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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -37,6 +37,9 @@ struct config {
|
|||
int sort_keys;
|
||||
int strip;
|
||||
int use_env;
|
||||
int have_hashseed;
|
||||
int hashseed;
|
||||
int precision;
|
||||
} conf;
|
||||
|
||||
#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t')
|
||||
|
@ -106,8 +109,16 @@ 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")) {
|
||||
conf.have_hashseed = 1;
|
||||
conf.hashseed = atoi(val);
|
||||
} else {
|
||||
conf.have_hashseed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
@ -168,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);
|
||||
|
||||
|
@ -188,6 +199,18 @@ 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);
|
||||
|
||||
if (conf.strip) {
|
||||
/* Load to memory, strip leading and trailing whitespace */
|
||||
buffer = loadfile(infile);
|
||||
|
@ -234,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;
|
||||
|
@ -247,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);
|
||||
|
||||
|
@ -265,22 +287,37 @@ int use_env()
|
|||
flags |= JSON_PRESERVE_ORDER;
|
||||
|
||||
if(getenv_int("JSON_SORT_KEYS"))
|
||||
flags |= 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,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.
|
||||
|
@ -194,6 +194,24 @@ static void encode_nul_byte()
|
|||
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 run_tests()
|
||||
{
|
||||
encode_null();
|
||||
|
@ -202,4 +220,5 @@ static void run_tests()
|
|||
encode_other_than_array_or_object();
|
||||
escape_slashes();
|
||||
encode_nul_byte();
|
||||
dump_file();
|
||||
}
|
||||
|
|
|
@ -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,18 @@ 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()
|
||||
|
@ -177,6 +205,7 @@ static void position()
|
|||
static void run_tests()
|
||||
{
|
||||
file_not_found();
|
||||
very_long_file_name();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
decode_any();
|
||||
|
|
|
@ -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,28 +23,74 @@ static void create_and_free_complex_object()
|
|||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void create_and_free_object_with_oom()
|
||||
{
|
||||
int i;
|
||||
char key[4];
|
||||
json_t *obj = json_object();
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
snprintf(key, sizeof key, "%d", i);
|
||||
json_object_set_new(obj, key, json_integer(i));
|
||||
}
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void *my_malloc(size_t size)
|
||||
{
|
||||
malloc_called += 1;
|
||||
malloc_called = 1;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void my_free(void *ptr)
|
||||
{
|
||||
free_called += 1;
|
||||
free_called = 1;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void test_simple()
|
||||
{
|
||||
json_malloc_t mfunc = NULL;
|
||||
json_free_t ffunc = NULL;
|
||||
|
||||
json_set_alloc_funcs(my_malloc, my_free);
|
||||
json_get_alloc_funcs(&mfunc, &ffunc);
|
||||
create_and_free_complex_object();
|
||||
|
||||
if(malloc_called != 20 || free_called != 20)
|
||||
if (malloc_called != 1 || free_called != 1
|
||||
|| mfunc != my_malloc || ffunc != my_free)
|
||||
fail("Custom allocation failed");
|
||||
}
|
||||
|
||||
|
||||
static void *oom_malloc(size_t size)
|
||||
{
|
||||
if (malloc_used + size > 800)
|
||||
return NULL;
|
||||
|
||||
malloc_used += size;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void oom_free(void *ptr)
|
||||
{
|
||||
free_called++;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void test_oom()
|
||||
{
|
||||
free_called = 0;
|
||||
json_set_alloc_funcs(oom_malloc, oom_free);
|
||||
create_and_free_object_with_oom();
|
||||
|
||||
if (free_called == 0)
|
||||
fail("Allocation with OOM failed");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Test the secure memory functions code given in the API reference
|
||||
documentation, but by using plain memset instead of
|
||||
|
@ -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;
|
||||
|
@ -262,7 +288,7 @@ static void test_iterators()
|
|||
foo = json_string("foo");
|
||||
bar = json_string("bar");
|
||||
baz = json_string("baz");
|
||||
if(!object || !foo || !bar || !bar)
|
||||
if(!object || !foo || !bar || !baz)
|
||||
fail("unable to create values");
|
||||
|
||||
if(json_object_iter_next(object, NULL))
|
||||
|
@ -276,26 +302,26 @@ static void test_iterators()
|
|||
iter = json_object_iter(object);
|
||||
if(!iter)
|
||||
fail("unable to get iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "a"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != foo)
|
||||
fail("iterating failed: wrong value");
|
||||
if (strcmp(json_object_iter_key(iter), "a") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != foo)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "b"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
if (strcmp(json_object_iter_key(iter), "b") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != bar)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
if (strcmp(json_object_iter_key(iter), "c") != 0)
|
||||
fail("iterating doesn't yield keys in order");
|
||||
if (json_object_iter_value(iter) != baz)
|
||||
fail("iterating doesn't yield values in order");
|
||||
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
fail("able to iterate over the end");
|
||||
|
@ -312,22 +338,14 @@ static void test_iterators()
|
|||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
if(json_object_iter_set(object, iter, bar))
|
||||
if(json_object_iter_set(object, iter, baz))
|
||||
fail("unable to set value at iterator");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
if(strcmp(json_object_iter_key(iter), "b"))
|
||||
fail("json_object_iter_key() fails after json_object_iter_set()");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("json_object_iter_value() fails after json_object_iter_set()");
|
||||
if(json_object_get(object, "c") != bar)
|
||||
if(json_object_get(object, "b") != baz)
|
||||
fail("json_object_get() fails after json_object_iter_set()");
|
||||
|
||||
json_decref(object);
|
||||
|
@ -358,6 +376,12 @@ static void test_misc()
|
|||
if(!json_object_set(object, NULL, string))
|
||||
fail("able to set NULL key");
|
||||
|
||||
if(json_object_del(object, "a"))
|
||||
fail("unable to del the only key");
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_object_set(object, "a", NULL))
|
||||
fail("able to set NULL value");
|
||||
|
||||
|
@ -497,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,6 +83,22 @@ static void run_tests()
|
|||
fail("json_pack string refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* nullable string (defined case) */
|
||||
value = json_pack("s?", "test");
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack nullable string (defined case) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack nullable string (defined case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* nullable string (NULL case) */
|
||||
value = json_pack("s?", NULL);
|
||||
if(!json_is_null(value))
|
||||
fail("json_pack nullable string (NULL case) failed");
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("json_pack nullable string (NULL case) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (int) */
|
||||
value = json_pack("s#", "test asdf", 4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
|
@ -163,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)
|
||||
|
@ -172,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.
|
||||
|
@ -224,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
|
||||
|
@ -298,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);
|
||||
|
@ -335,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 */
|
||||
|
@ -378,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,2 +1,3 @@
|
|||
JSON_COMPACT=1
|
||||
export JSON_COMPACT
|
||||
HASHSEED=1
|
||||
export JSON_COMPACT HASHSEED
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
JSON_INDENT=4
|
||||
JSON_COMPACT=1
|
||||
export JSON_INDENT JSON_COMPACT
|
||||
HASHSEED=1
|
||||
export JSON_INDENT JSON_COMPACT HASHSEED
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
JSON_INDENT=4
|
||||
export JSON_INDENT
|
||||
HASHSEED=1
|
||||
export JSON_INDENT HASHSEED
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
HASHSEED=1
|
||||
export HASHSEED
|
|
@ -0,0 +1,2 @@
|
|||
JSON_REAL_PRECISION=4
|
||||
export JSON_REAL_PRECISION
|
|
@ -0,0 +1 @@
|
|||
[1.23456789, 1.0, 1.0000000000000002]
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
1 5 5
|
||||
invalid escape near '"\uq'
|
|
@ -0,0 +1 @@
|
|||
["\uqqqq <-- invalid unicode escape"]
|
|
@ -0,0 +1,2 @@
|
|||
1 33 33
|
||||
\u0000 is not allowed without JSON_ALLOW_NUL
|
|
@ -0,0 +1 @@
|
|||
["null escape \u0000 not allowed"]
|
|
@ -0,0 +1,2 @@
|
|||
1 2049 2049
|
||||
maximum parsing depth reached near '['
|
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.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
[1.8011670033376514e-308]
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue