Mercurial > code
changeset 523:d7d434ab9114
Json: resurrection
line wrap: on
line diff
--- a/CMakeLists.txt Wed Jun 01 17:06:50 2016 +0200 +++ b/CMakeLists.txt Wed Jun 01 17:09:12 2016 +0200 @@ -48,4 +48,5 @@ add_subdirectory(modules/hash) add_subdirectory(modules/ini) add_subdirectory(modules/js) +add_subdirectory(modules/json) add_subdirectory(modules/options)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/CMakeLists.txt Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,26 @@ +# +# CMakeLists.txt -- code building for common code +# +# Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +add_subdirectory(jansson) + +code_define_module( + NAME json + SOURCES json.cpp json.hpp + LIBRARIES jansson + FLAGS DIRECTORY=\"${CMAKE_CURRENT_SOURCE_DIR}/test/data/\" +) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/doc/mainpage.cpp Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,18 @@ +/** + * \mainpage + * + * Welcome to the json library. + * + * ## Introduction + * + * This C++14 wrapper helps you reading / writing JSON files using the libjansson library. + * + * ## Requirements + * + * - C++14, + * - [Jansson](http://www.digip.org/jansson). + * + * ## Installation + * + * Just copy the two files json.hpp and json.cpp and add them to your project. + */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/CMakeLists.txt Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,268 @@ +# +# CMakeLists.txt -- CMake build system for jansson +# +# Copyright (c) 2016 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +# +# This CMakeLists has been modified from vanilla jansson source. +# ------------------------------------------------------------------- +# +# - installation stuff has been removed, +# - documentation process has been removed. +# - style has been adapted to match our conventions. +# - comments and unneeded stuff have been removed. +# + +cmake_minimum_required(VERSION 3.0) +project(jansson C) + +option(USE_URANDOM "Use /dev/urandom to seed the hash function." On) +option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." On) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${jansson_SOURCE_DIR}/cmake) + +include(CheckCSourceCompiles) +include(CheckFunctionExists) +include(CheckFunctionKeywords) +include(CheckIncludeFiles) +include(CheckTypeSize) + +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(stdint.h HAVE_STDINT_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_type_size(__int64 __INT64) +check_type_size(int64_t INT64_T) +check_type_size("long long" LONG_LONG_INT) +check_type_size(int32_t INT32_T) +check_type_size(__int32 __INT32) +check_type_size("long" LONG_INT) +check_type_size("int" INT) + +if (HAVE_INT32_T) + set(JSON_INT32 int32_t) +elseif (HAVE___INT32) + set(JSON_INT32 __int32) +elseif (HAVE_LONG_INT AND (${LONG_INT} EQUAL 4)) + set(JSON_INT32 long) +elseif (HAVE_INT AND (${INT} EQUAL 4)) + set(JSON_INT32 int) +else () + message (FATAL_ERROR "Could not detect a valid 32-bit integer type") +endif () + +check_type_size("unsigned long" UNSIGNED_LONG_INT) +check_type_size("unsigned int" UNSIGNED_INT) +check_type_size("unsigned short" UNSIGNED_SHORT) +check_type_size(uint32_t UINT32_T) +check_type_size(__uint32 __UINT32) + +if (HAVE_UINT32_T) + set(JSON_UINT32 uint32_t) +elseif (HAVE___UINT32) + set(JSON_UINT32 __uint32) +elseif (HAVE_UNSIGNED_LONG_INT AND (${UNSIGNED_LONG_INT} EQUAL 4)) + set(JSON_UINT32 "unsigned long") +elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 4)) + set(JSON_UINT32 "unsigned int") +else () + message(FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type") +endif () + +check_type_size(uint16_t UINT16_T) +check_type_size(__uint16 __UINT16) + +if (HAVE_UINT16_T) + set(JSON_UINT16 uint16_t) +elseif (HAVE___UINT16) + set(JSON_UINT16 __uint16) +elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 2)) + set(JSON_UINT16 "unsigned int") +elseif (HAVE_UNSIGNED_SHORT AND (${UNSIGNED_SHORT} EQUAL 2)) + set(JSON_UINT16 "unsigned short") +else () + message(FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type") +endif () + +check_type_size(uint8_t UINT8_T) +check_type_size(__uint8 __UINT8) + +if (HAVE_UINT8_T) + set(JSON_UINT8 uint8_t) +elseif (HAVE___UINT8) + set(JSON_UINT8 __uint8) +else () + set(JSON_UINT8 "unsigned char") +endif () + +check_type_size(ssize_t SSIZE_T) +check_type_size(SSIZE_T UPPERCASE_SSIZE_T) + +if(NOT HAVE_SSIZE_T) + if(HAVE_UPPERCASE_SSIZE_T) + set(JSON_SSIZE SSIZE_T) + else() + set(JSON_SSIZE int) + endif() +endif() + +set(CMAKE_EXTRA_INCLUDE_FILES "") + +check_function_exists(strtoll HAVE_STRTOLL) +check_function_exists(strtoq HAVE_STRTOQ) +check_function_exists(_strtoi64 HAVE__STRTOI64) + +if (HAVE_STRTOLL) + set(JSON_STRTOINT strtoll) +elseif (HAVE_STRTOQ) + set(JSON_STRTOINT strtoq) +elseif (HAVE__STRTOI64) + set(JSON_STRTOINT _strtoi64) +else () + set (JSON_STRTOINT strtol) + set (JSON_INT_T long) + set (JSON_INTEGER_FORMAT "\"ld\"") +endif () + +if (NOT DEFINED JSON_INT_T) + if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8)) + set(JSON_INT_T "long long") + elseif (HAVE_INT64_T) + set(JSON_INT_T int64_t) + elseif (HAVE___INT64) + set(JSON_INT_T __int64) + else () + message(FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent") + endif () + + if (WIN32) + set(JSON_INTEGER_FORMAT "\"I64d\"") + else () + set(JSON_INTEGER_FORMAT "\"lld\"") + endif () +endif () + +check_include_files (locale.h HAVE_LOCALE_H) +check_function_exists (localeconv HAVE_LOCALECONV) + +if (HAVE_LOCALECONV AND HAVE_LOCALE_H) + set(JSON_HAVE_LOCALECONV 1) +else () + set(JSON_HAVE_LOCALECONV 0) +endif() + +check_function_exists(setlocale HAVE_SETLOCALE) + +check_function_keywords("inline") +check_function_keywords("__inline") +check_function_keywords("__inline__") + +if (HAVE_INLINE) + set(JSON_INLINE inline) +elseif (HAVE___INLINE) + set(JSON_INLINE __inline) +elseif (HAVE___INLINE__) + set(JSON_INLINE __inline__) +else() + set (JSON_INLINE) +endif() + +check_function_exists(snprintf HAVE_SNPRINTF) +check_function_exists(_snprintf HAVE__SNPRINTF) + +if (HAVE_SNPRINTF) + set(JSON_SNPRINTF snprintf) +elseif (HAVE__SNPRINTF) + set(JSON_SNPRINTF _snprintf) +endif () + +check_c_source_compiles("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS) +check_c_source_compiles("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS) + +configure_file( + ${jansson_SOURCE_DIR}/cmake/jansson_config.h.cmake + ${jansson_BINARY_DIR}/include/jansson_config.h +) + +file( + COPY ${jansson_SOURCE_DIR}/src/jansson.h + DESTINATION ${jansson_BINARY_DIR}/include/ +) + +add_definitions(-DJANSSON_USING_CMAKE) + +configure_file( + ${jansson_SOURCE_DIR}/cmake/jansson_private_config.h.cmake + ${jansson_BINARY_DIR}/private_include/jansson_private_config.h +) + +add_definitions(-DHAVE_CONFIG_H) + +set( + JANSSON_SRC + ${jansson_SOURCE_DIR}/src/dump.c + ${jansson_SOURCE_DIR}/src/error.c + ${jansson_SOURCE_DIR}/src/hashtable.c + ${jansson_SOURCE_DIR}/src/hashtable_seed.c + ${jansson_SOURCE_DIR}/src/load.c + ${jansson_SOURCE_DIR}/src/memory.c + ${jansson_SOURCE_DIR}/src/pack_unpack.c + ${jansson_SOURCE_DIR}/src/strbuffer.c + ${jansson_SOURCE_DIR}/src/strconv.c + ${jansson_SOURCE_DIR}/src/utf.c + ${jansson_SOURCE_DIR}/src/value.c +) + +set( + JANSSON_HDR_PRIVATE + ${jansson_SOURCE_DIR}/src/hashtable.h + ${jansson_SOURCE_DIR}/src/jansson_private.h + ${jansson_SOURCE_DIR}/src/strbuffer.h + ${jansson_SOURCE_DIR}/src/utf.h + ${jansson_BINARY_DIR}/private_include/jansson_private_config.h +) + +set( + JANSSON_HDR_PUBLIC + ${jansson_BINARY_DIR}/include/jansson_config.h + ${jansson_SOURCE_DIR}/src/jansson.h +) + +add_library(jansson STATIC ${JANSSON_SRC} ${JANSSON_HDR_PRIVATE} ${JANSSON_HDR_PUBLIC}) + +target_include_directories( + jansson + PUBLIC ${jansson_BINARY_DIR}/include + PRIVATE ${jansson_BINARY_DIR}/private_include +) + +if (MSVC) + target_compile_definitions(jansson PUBLIC _CRT_SECURE_NO_WARNINGS) + target_compile_options(jansson PUBLIC /W3 /D /wd4005 /wd4996 /wd4334 /wd4267) +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/cmake/CheckFunctionKeywords.cmake Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/cmake/jansson_config.h.cmake Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The CMake system will generate the jansson_config.h file and + * copy it to the build and install directories. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* Define this so that we can disable scattered automake configuration in source files */ +#ifndef JANSSON_USING_CMAKE +#define JANSSON_USING_CMAKE +#endif + +/* Note: when using cmake, JSON_INTEGER_IS_LONG_LONG is not defined nor used, + * as we will also check for __int64 etc types. + * (the definition was used in the automake system) */ + +/* Bring in the cmake-detected defines */ +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Include our standard type header for the integer typedef */ + +#if defined(HAVE_STDINT_H) +# include <stdint.h> +#elif defined(HAVE_INTTYPES_H) +# include <inttypes.h> +#elif defined(HAVE_SYS_TYPES_H) +# include <sys/types.h> +#endif + + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @JSON_INLINE@ +#endif + + +#define json_int_t @JSON_INT_T@ +#define json_strtoint @JSON_STRTOINT@ +#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@ + + +/* If locale.h and localeconv() are available, define to 1, otherwise to 0. */ +#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@ + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/cmake/jansson_private_config.h.cmake Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,73 @@ +#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 HAVE_SNPRINTF 1 + +/* snprintf should not be defined as macro with MSC_VER >= 1900 */ +#if defined(_WIN32) || defined(WIN32) +# if defined(_MSC_VER) /* MS compiller */ +# if (_MSC_VER < 1900) /* snprintf not introduced */ +# if !defined(snprintf) +# define snprintf _snprintf +# define HAVE_SNPRINTF 1 /* snprintf defined manually */ +# endif +# else +# define HAVE_SNPRINTF 1 /* snprintf available via sdk */ +# endif +# endif +#endif + +#ifndef HAVE_SNPRINTF +# define snprintf @JSON_SNPRINTF@ +#endif + +#cmakedefine HAVE_VSNPRINTF + +#cmakedefine USE_URANDOM 1 +#cmakedefine USE_WINDOWS_CRYPTOAPI 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/patch/snprintf.patch Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,53 @@ +diff --git a/cmake/jansson_private_config.h.cmake b/cmake/jansson_private_config.h.cmake +index ee1078f..ac7318f 100644 +--- a/cmake/jansson_private_config.h.cmake ++++ b/cmake/jansson_private_config.h.cmake +@@ -49,6 +49,20 @@ + + #cmakedefine HAVE_SNPRINTF 1 + ++/* snprintf should not be defined as macro with MSC_VER >= 1900 */ ++#if defined(_WIN32) || defined(WIN32) ++# if defined(_MSC_VER) /* MS compiller */ ++# if (_MSC_VER < 1900) /* snprintf not introduced */ ++# if !defined(snprintf) ++# define snprintf _snprintf ++# define HAVE_SNPRINTF 1 /* snprintf defined manually */ ++# endif ++# else ++# define HAVE_SNPRINTF 1 /* snprintf available via sdk */ ++# endif ++# endif ++#endif ++ + #ifndef HAVE_SNPRINTF + # define snprintf @JSON_SNPRINTF@ + #endif +diff --git a/src/jansson_private.h b/src/jansson_private.h +index e100726..ccb3a57 100644 +--- a/src/jansson_private.h ++++ b/src/jansson_private.h +@@ -90,10 +90,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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/dump.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "jansson.h" +#include "jansson_private.h" +#include "strbuffer.h" +#include "utf.h" + +#define MAX_INTEGER_STR_LENGTH 100 +#define MAX_REAL_STR_LENGTH 100 + +#define FLAGS_TO_INDENT(f) ((f) & 0x1F) +#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) + +struct object_key { + size_t serial; + const char *key; +}; + +static int dump_to_strbuffer(const char *buffer, size_t size, void *data) +{ + return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); +} + +static int dump_to_file(const char *buffer, size_t size, void *data) +{ + FILE *dest = (FILE *)data; + if(fwrite(buffer, size, 1, dest) != 1) + return -1; + return 0; +} + +/* 32 spaces (the maximum indentation size) */ +static const char whitespace[] = " "; + +static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) +{ + if(FLAGS_TO_INDENT(flags) > 0) + { + int i, ws_count = FLAGS_TO_INDENT(flags); + + if(dump("\n", 1, data)) + return -1; + + for(i = 0; i < depth; i++) + { + if(dump(whitespace, ws_count, data)) + return -1; + } + } + else if(space && !(flags & JSON_COMPACT)) + { + return dump(" ", 1, data); + } + return 0; +} + +static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags) +{ + const char *pos, *end, *lim; + int32_t codepoint; + + if(dump("\"", 1, data)) + return -1; + + end = pos = str; + lim = str + len; + while(1) + { + const char *text; + char seq[13]; + int length; + + while(end < lim) + { + end = utf8_iterate(pos, lim - pos, &codepoint); + if(!end) + return -1; + + /* mandatory escape or control char */ + if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) + break; + + /* slash */ + if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') + break; + + /* non-ASCII */ + if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) + break; + + pos = end; + } + + if(pos != str) { + if(dump(str, pos - str, data)) + return -1; + } + + if(end == pos) + break; + + /* handle \, /, ", and control codes */ + length = 2; + switch(codepoint) + { + case '\\': text = "\\\\"; break; + case '\"': text = "\\\""; break; + case '\b': text = "\\b"; break; + case '\f': text = "\\f"; break; + case '\n': text = "\\n"; break; + case '\r': text = "\\r"; break; + case '\t': text = "\\t"; break; + case '/': text = "\\/"; break; + default: + { + /* codepoint is in BMP */ + if(codepoint < 0x10000) + { + sprintf(seq, "\\u%04X", codepoint); + length = 6; + } + + /* not in BMP -> construct a UTF-16 surrogate pair */ + else + { + int32_t first, last; + + codepoint -= 0x10000; + first = 0xD800 | ((codepoint & 0xffc00) >> 10); + last = 0xDC00 | (codepoint & 0x003ff); + + sprintf(seq, "\\u%04X\\u%04X", first, last); + length = 12; + } + + text = seq; + break; + } + } + + if(dump(text, length, data)) + return -1; + + str = pos = end; + } + + return dump("\"", 1, data); +} + +static int object_key_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; +} + +static int do_dump(const json_t *json, size_t flags, int depth, + json_dump_callback_t dump, void *data) +{ + if(!json) + return -1; + + switch(json_typeof(json)) { + case JSON_NULL: + return dump("null", 4, data); + + case JSON_TRUE: + return dump("true", 4, data); + + case JSON_FALSE: + return dump("false", 5, data); + + case JSON_INTEGER: + { + char buffer[MAX_INTEGER_STR_LENGTH]; + int size; + + size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, + "%" JSON_INTEGER_FORMAT, + json_integer_value(json)); + if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) + return -1; + + return dump(buffer, size, data); + } + + case JSON_REAL: + { + char buffer[MAX_REAL_STR_LENGTH]; + int size; + double value = json_real_value(json); + + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, + FLAGS_TO_PRECISION(flags)); + if(size < 0) + return -1; + + return dump(buffer, size, data); + } + + case JSON_STRING: + return dump_string(json_string_value(json), json_string_length(json), dump, data, flags); + + case JSON_ARRAY: + { + int i; + int n; + json_array_t *array; + + /* detect circular references */ + array = json_to_array(json); + if(array->visited) + goto array_error; + array->visited = 1; + + n = json_array_size(json); + + if(dump("[", 1, data)) + goto array_error; + if(n == 0) { + array->visited = 0; + return dump("]", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + goto array_error; + + for(i = 0; i < n; ++i) { + if(do_dump(json_array_get(json, i), flags, depth + 1, + dump, data)) + goto array_error; + + if(i < n - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + goto array_error; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + goto array_error; + } + } + + array->visited = 0; + return dump("]", 1, data); + + array_error: + array->visited = 0; + return -1; + } + + case JSON_OBJECT: + { + json_object_t *object; + void *iter; + const char *separator; + int separator_length; + + if(flags & JSON_COMPACT) { + separator = ":"; + separator_length = 1; + } + else { + separator = ": "; + separator_length = 2; + } + + /* detect circular references */ + object = json_to_object(json); + if(object->visited) + goto object_error; + object->visited = 1; + + iter = json_object_iter((json_t *)json); + + if(dump("{", 1, data)) + goto object_error; + if(!iter) { + object->visited = 0; + return dump("}", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + goto object_error; + + if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER) + { + struct object_key *keys; + size_t size, i; + int (*cmp_func)(const void *, const void *); + + size = json_object_size(json); + keys = jsonp_malloc(size * sizeof(struct object_key)); + if(!keys) + goto object_error; + + i = 0; + while(iter) + { + keys[i].serial = hashtable_iter_serial(iter); + keys[i].key = json_object_iter_key(iter); + iter = json_object_iter_next((json_t *)json, iter); + i++; + } + 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); + + for(i = 0; i < size; i++) + { + const char *key; + json_t *value; + + key = keys[i].key; + value = json_object_get(json, key); + assert(value); + + dump_string(key, strlen(key), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(value, flags, depth + 1, dump, data)) + { + jsonp_free(keys); + goto object_error; + } + + if(i < size - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + { + jsonp_free(keys); + goto object_error; + } + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + { + jsonp_free(keys); + goto object_error; + } + } + } + + jsonp_free(keys); + } + else + { + /* Don't sort keys */ + + while(iter) + { + void *next = json_object_iter_next((json_t *)json, iter); + const char *key = json_object_iter_key(iter); + + dump_string(key, strlen(key), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(json_object_iter_value(iter), flags, depth + 1, + dump, data)) + goto object_error; + + if(next) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + goto object_error; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + goto object_error; + } + + iter = next; + } + } + + object->visited = 0; + return dump("}", 1, data); + + object_error: + object->visited = 0; + return -1; + } + + default: + /* not reached */ + return -1; + } +} + +char *json_dumps(const json_t *json, size_t flags) +{ + strbuffer_t strbuff; + char *result; + + if(strbuffer_init(&strbuff)) + return NULL; + + if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) + result = NULL; + else + result = jsonp_strdup(strbuffer_value(&strbuff)); + + strbuffer_close(&strbuff); + return result; +} + +int json_dumpf(const json_t *json, FILE *output, size_t flags) +{ + return json_dump_callback(json, dump_to_file, (void *)output, flags); +} + +int json_dump_file(const json_t *json, const char *path, size_t flags) +{ + int result; + + FILE *output = fopen(path, "w"); + if(!output) + return -1; + + result = json_dumpf(json, output, flags); + + fclose(output); + return result; +} + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) +{ + if(!(flags & JSON_ENCODE_ANY)) { + if(!json_is_array(json) && !json_is_object(json)) + return -1; + } + + return do_dump(json, flags, 0, callback, data); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/error.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,63 @@ +#include <string.h> +#include "jansson_private.h" + +void jsonp_error_init(json_error_t *error, const char *source) +{ + if(error) + { + error->text[0] = '\0'; + error->line = -1; + error->column = -1; + error->position = 0; + if(source) + jsonp_error_set_source(error, source); + else + error->source[0] = '\0'; + } +} + +void jsonp_error_set_source(json_error_t *error, const char *source) +{ + size_t length; + + if(!error || !source) + return; + + length = strlen(source); + if(length < JSON_ERROR_SOURCE_LENGTH) + strcpy(error->source, source); + else { + size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; + strcpy(error->source, "..."); + strcpy(error->source + 3, source + extra); + } +} + +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + jsonp_error_vset(error, line, column, position, msg, ap); + va_end(ap); +} + +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, const char *msg, va_list ap) +{ + if(!error) + return; + + if(error->text[0] != '\0') { + /* error already set */ + return; + } + + error->line = line; + error->column = column; + error->position = position; + + vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap); + error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/hashtable.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2009-2014 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" + +typedef struct hashtable_list list_t; +typedef struct hashtable_pair pair_t; +typedef struct hashtable_bucket bucket_t; + +extern volatile uint32_t hashtable_seed; + +/* Implementation of the hash function */ +#include "lookup3.h" + +#define list_to_pair(list_) container_of(list_, pair_t, list) +#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed)) + +static JSON_INLINE void list_init(list_t *list) +{ + list->next = list; + list->prev = list; +} + +static JSON_INLINE void list_insert(list_t *list, list_t *node) +{ + node->next = list; + node->prev = list->prev; + list->prev->next = node; + list->prev = node; +} + +static JSON_INLINE void list_remove(list_t *list) +{ + list->prev->next = list->next; + list->next->prev = list->prev; +} + +static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) +{ + return bucket->first == &hashtable->list && bucket->first == bucket->last; +} + +static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, + list_t *list) +{ + if(bucket_is_empty(hashtable, bucket)) + { + list_insert(&hashtable->list, list); + bucket->first = bucket->last = list; + } + else + { + list_insert(bucket->first, list); + bucket->first = list; + } +} + +static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, + const char *key, size_t hash) +{ + list_t *list; + pair_t *pair; + + if(bucket_is_empty(hashtable, bucket)) + return NULL; + + list = bucket->first; + while(1) + { + pair = list_to_pair(list); + if(pair->hash == hash && strcmp(pair->key, key) == 0) + return pair; + + if(list == bucket->last) + break; + + list = list->next; + } + + return NULL; +} + +/* returns 0 on success, -1 if key was not found */ +static int hashtable_do_del(hashtable_t *hashtable, + const char *key, size_t hash) +{ + pair_t *pair; + bucket_t *bucket; + size_t index; + + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return -1; + + if(&pair->list == bucket->first && &pair->list == bucket->last) + bucket->first = bucket->last = &hashtable->list; + + else if(&pair->list == bucket->first) + bucket->first = pair->list.next; + + else if(&pair->list == bucket->last) + bucket->last = pair->list.prev; + + list_remove(&pair->list); + json_decref(pair->value); + + jsonp_free(pair); + hashtable->size--; + + return 0; +} + +static void hashtable_do_clear(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + + for(list = hashtable->list.next; list != &hashtable->list; list = next) + { + next = list->next; + pair = list_to_pair(list); + json_decref(pair->value); + jsonp_free(pair); + } +} + +static int hashtable_do_rehash(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + size_t i, index, new_size; + + jsonp_free(hashtable->buckets); + + hashtable->order++; + new_size = hashsize(hashtable->order); + + hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t)); + if(!hashtable->buckets) + return -1; + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list = hashtable->list.next; + list_init(&hashtable->list); + + for(; list != &hashtable->list; list = next) { + next = list->next; + pair = list_to_pair(list); + index = pair->hash % new_size; + insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); + } + + return 0; +} + + +int hashtable_init(hashtable_t *hashtable) +{ + size_t i; + + hashtable->size = 0; + hashtable->order = 3; + hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t)); + if(!hashtable->buckets) + return -1; + + list_init(&hashtable->list); + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + return 0; +} + +void hashtable_close(hashtable_t *hashtable) +{ + hashtable_do_clear(hashtable); + jsonp_free(hashtable->buckets); +} + +int hashtable_set(hashtable_t *hashtable, + const char *key, size_t serial, + json_t *value) +{ + pair_t *pair; + bucket_t *bucket; + size_t hash, index; + + /* rehash if the load ratio exceeds 1 */ + if(hashtable->size >= hashsize(hashtable->order)) + if(hashtable_do_rehash(hashtable)) + return -1; + + hash = hash_str(key); + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + pair = hashtable_find_pair(hashtable, bucket, key, hash); + + if(pair) + { + json_decref(pair->value); + pair->value = value; + } + else + { + /* offsetof(...) returns the size of pair_t without the last, + flexible member. This way, the correct amount is + allocated. */ + + size_t len = strlen(key); + if(len >= (size_t)-1 - offsetof(pair_t, key)) { + /* Avoid an overflow if the key is very long */ + return -1; + } + + pair = jsonp_malloc(offsetof(pair_t, key) + len + 1); + if(!pair) + return -1; + + pair->hash = hash; + pair->serial = serial; + strcpy(pair->key, key); + pair->value = value; + list_init(&pair->list); + + insert_to_bucket(hashtable, bucket, &pair->list); + + hashtable->size++; + } + return 0; +} + +void *hashtable_get(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return pair->value; +} + +int hashtable_del(hashtable_t *hashtable, const char *key) +{ + size_t hash = hash_str(key); + return hashtable_do_del(hashtable, key, hash); +} + +void hashtable_clear(hashtable_t *hashtable) +{ + size_t i; + + hashtable_do_clear(hashtable); + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list_init(&hashtable->list); + hashtable->size = 0; +} + +void *hashtable_iter(hashtable_t *hashtable) +{ + return hashtable_iter_next(hashtable, &hashtable->list); +} + +void *hashtable_iter_at(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return &pair->list; +} + +void *hashtable_iter_next(hashtable_t *hashtable, void *iter) +{ + list_t *list = (list_t *)iter; + if(list->next == &hashtable->list) + return NULL; + return list->next; +} + +void *hashtable_iter_key(void *iter) +{ + pair_t *pair = 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); + return pair->value; +} + +void hashtable_iter_set(void *iter, json_t *value) +{ + pair_t *pair = list_to_pair((list_t *)iter); + + json_decref(pair->value); + pair->value = value; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/hashtable.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2009-2014 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. + */ + +#ifndef HASHTABLE_H +#define HASHTABLE_H + +struct hashtable_list { + struct hashtable_list *prev; + struct hashtable_list *next; +}; + +/* "pair" may be a bit confusing a name, but think of it as a + key-value pair. In this case, it just encodes some extra data, + too */ +struct hashtable_pair { + size_t hash; + struct hashtable_list list; + json_t *value; + size_t serial; + char key[1]; +}; + +struct hashtable_bucket { + struct hashtable_list *first; + struct hashtable_list *last; +}; + +typedef struct hashtable { + size_t size; + struct hashtable_bucket *buckets; + size_t order; /* hashtable has pow(2, order) buckets */ + struct hashtable_list list; +} hashtable_t; + + +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->list)) + + +/** + * hashtable_init - Initialize a hashtable object + * + * @hashtable: The (statically allocated) hashtable object + * + * Initializes a statically allocated hashtable object. The object + * should be cleared with hashtable_close when it's no longer used. + * + * Returns 0 on success, -1 on error (out of memory). + */ +int hashtable_init(hashtable_t *hashtable); + +/** + * hashtable_close - Release all resources used by a hashtable object + * + * @hashtable: The hashtable + * + * Destroys a statically allocated hashtable object. + */ +void hashtable_close(hashtable_t *hashtable); + +/** + * hashtable_set - Add/modify value in hashtable + * + * @hashtable: The hashtable object + * @key: The key + * @serial: For addition order of keys + * @value: The value + * + * If a value with the given key already exists, its value is replaced + * with the new value. Value is "stealed" in the sense that hashtable + * doesn't increment its refcount but decreases the refcount when the + * value is no longer needed. + * + * Returns 0 on success, -1 on failure (out of memory). + */ +int hashtable_set(hashtable_t *hashtable, + const char *key, size_t serial, + json_t *value); + +/** + * hashtable_get - Get a value associated with a key + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns value if it is found, or NULL otherwise. + */ +void *hashtable_get(hashtable_t *hashtable, const char *key); + +/** + * hashtable_del - Remove a value from the hashtable + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns 0 on success, or -1 if the key was not found. + */ +int hashtable_del(hashtable_t *hashtable, const char *key); + +/** + * hashtable_clear - Clear hashtable + * + * @hashtable: The hashtable object + * + * Removes all items from the hashtable. + */ +void hashtable_clear(hashtable_t *hashtable); + +/** + * hashtable_iter - Iterate over hashtable + * + * @hashtable: The hashtable object + * + * Returns an opaque iterator to the first element in the hashtable. + * The iterator should be passed to hashtable_iter_* functions. + * The hashtable items are not iterated over in any particular order. + * + * There's no need to free the iterator in any way. The iterator is + * valid as long as the item that is referenced by the iterator is not + * deleted. Other values may be added or deleted. In particular, + * hashtable_iter_next() may be called on an iterator, and after that + * the key/value pair pointed by the old iterator may be deleted. + */ +void *hashtable_iter(hashtable_t *hashtable); + +/** + * hashtable_iter_at - Return an iterator at a specific key + * + * @hashtable: The hashtable object + * @key: The key that the iterator should point to + * + * Like hashtable_iter() but returns an iterator pointing to a + * specific key. + */ +void *hashtable_iter_at(hashtable_t *hashtable, const char *key); + +/** + * hashtable_iter_next - Advance an iterator + * + * @hashtable: The hashtable object + * @iter: The iterator + * + * Returns a new iterator pointing to the next element in the + * hashtable or NULL if the whole hastable has been iterated over. + */ +void *hashtable_iter_next(hashtable_t *hashtable, void *iter); + +/** + * hashtable_iter_key - Retrieve the key pointed by an iterator + * + * @iter: The iterator + */ +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 + * + * @iter: The iterator + */ +void *hashtable_iter_value(void *iter); + +/** + * hashtable_iter_set - Set the value pointed by an iterator + * + * @iter: The iterator + * @value: The value to set + */ +void hashtable_iter_set(void *iter, json_t *value); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/hashtable_seed.c Wed Jun 01 17:09:12 2016 +0200 @@ -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 (!done && seed_from_urandom(&seed) == 0) + done = 1; +#endif + +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) + if (!done && seed_from_windows_cryptoapi(&seed) == 0) + done = 1; +#endif + + if (!done) { + /* Fall back to timestamp and PID if no better randomness is + available */ + seed_from_timestamp_and_pid(&seed); + } + + /* Make sure the seed is never zero */ + if (seed == 0) + seed = 1; + + return seed; +} + + +volatile uint32_t hashtable_seed = 0; + +#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +static volatile char seed_initialized = 0; + +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); + } else { + /* Wait for another thread to do the seeding */ + do { +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); + } + } +} +#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) { + /* Explicit synchronization fences are not supported by the + __sync builtins, so every thread getting here has to + generate the seed value. + */ + new_seed = generate_seed(); + } + + do { + if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) { + /* We were the first to seed */ + break; + } else { + /* Wait for another thread to do the seeding */ +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } + } while(hashtable_seed == 0); + } +} +#elif defined(_WIN32) +static long seed_initialized = 0; +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (InterlockedIncrement(&seed_initialized) == 1) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } else { + /* Wait for another thread to do the seeding */ + do { + SwitchToThread(); + } while (hashtable_seed == 0); + } + } +} +#else +/* Fall back to a thread-unsafe version */ +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/jansson.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_H +#define JANSSON_H + +#include <stdio.h> +#include <stdlib.h> /* for size_t */ +#include <stdarg.h> + +#include <jansson_config.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ + +#define JANSSON_MAJOR_VERSION 2 +#define JANSSON_MINOR_VERSION 7 +#define JANSSON_MICRO_VERSION 0 + +/* Micro version is omitted if it's 0 */ +#define JANSSON_VERSION "2.7" + +/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this + for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ +#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \ + (JANSSON_MINOR_VERSION << 8) | \ + (JANSSON_MICRO_VERSION << 0)) + + +/* types */ + +typedef enum { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} json_type; + +typedef struct json_t { + json_type type; + size_t refcount; +} json_t; + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else +#define JSON_INTEGER_FORMAT "lld" +#endif +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif + +#define json_typeof(json) ((json)->type) +#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT) +#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY) +#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) +#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) +#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) +#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) +#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) +#define json_boolean_value json_is_true +#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) +#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL) + +/* construction, destruction, reference counting */ + +json_t *json_object(void); +json_t *json_array(void); +json_t *json_string(const char *value); +json_t *json_stringn(const char *value, size_t len); +json_t *json_string_nocheck(const char *value); +json_t *json_stringn_nocheck(const char *value, size_t len); +json_t *json_integer(json_int_t value); +json_t *json_real(double value); +json_t *json_true(void); +json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) +json_t *json_null(void); + +static JSON_INLINE +json_t *json_incref(json_t *json) +{ + if(json && json->refcount != (size_t)-1) + ++json->refcount; + return json; +} + +/* do not call json_delete directly */ +void json_delete(json_t *json); + +static JSON_INLINE +void json_decref(json_t *json) +{ + if(json && json->refcount != (size_t)-1 && --json->refcount == 0) + json_delete(json); +} + + +/* error reporting */ + +#define JSON_ERROR_TEXT_LENGTH 160 +#define JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct { + int line; + int column; + int position; + char source[JSON_ERROR_SOURCE_LENGTH]; + char text[JSON_ERROR_TEXT_LENGTH]; +} json_error_t; + + +/* 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); +int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); +int json_object_del(json_t *object, const char *key); +int json_object_clear(json_t *object); +int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); +void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); +void *json_object_iter_next(json_t *object, void *iter); +const char *json_object_iter_key(void *iter); +json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); + +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) + +#define json_array_foreach(array, index, value) \ + for(index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) + +static JSON_INLINE +int json_object_set(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_set_nocheck(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new_nocheck(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_iter_set(json_t *object, void *iter, json_t *value) +{ + return json_object_iter_set_new(object, iter, json_incref(value)); +} + +size_t json_array_size(const json_t *array); +json_t *json_array_get(const json_t *array, size_t index); +int json_array_set_new(json_t *array, size_t index, json_t *value); +int json_array_append_new(json_t *array, json_t *value); +int json_array_insert_new(json_t *array, size_t index, json_t *value); +int json_array_remove(json_t *array, size_t index); +int json_array_clear(json_t *array); +int json_array_extend(json_t *array, json_t *other); + +static JSON_INLINE +int json_array_set(json_t *array, size_t ind, json_t *value) +{ + return json_array_set_new(array, ind, json_incref(value)); +} + +static JSON_INLINE +int json_array_append(json_t *array, json_t *value) +{ + return json_array_append_new(array, json_incref(value)); +} + +static JSON_INLINE +int json_array_insert(json_t *array, size_t ind, json_t *value) +{ + return json_array_insert_new(array, ind, json_incref(value)); +} + +const char *json_string_value(const json_t *string); +size_t json_string_length(const json_t *string); +json_int_t json_integer_value(const json_t *integer); +double json_real_value(const json_t *real); +double json_number_value(const json_t *json); + +int json_string_set(json_t *string, const char *value); +int json_string_setn(json_t *string, const char *value, size_t len); +int json_string_set_nocheck(json_t *string, const char *value); +int json_string_setn_nocheck(json_t *string, const char *value, size_t len); +int json_integer_set(json_t *integer, json_int_t value); +int json_real_set(json_t *real, double value); + +/* pack, unpack */ + +json_t *json_pack(const char *fmt, ...); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); + +#define JSON_VALIDATE_ONLY 0x1 +#define JSON_STRICT 0x2 + +int json_unpack(json_t *root, const char *fmt, ...); +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap); + + +/* equality */ + +int json_equal(json_t *value1, json_t *value2); + + +/* copying */ + +json_t *json_copy(json_t *value); +json_t *json_deep_copy(const json_t *value); + + +/* decoding */ + +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 +#define JSON_ALLOW_NUL 0x10 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + +json_t *json_loads(const char *input, size_t flags, json_error_t *error); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); + + +/* encoding */ + +#define JSON_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); + +char *json_dumps(const json_t *json, size_t flags); +int json_dumpf(const json_t *json, FILE *output, size_t flags); +int json_dump_file(const json_t *json, const char *path, size_t flags); +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); + +/* custom memory allocation */ + +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); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/jansson_config.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE inline +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG 1 + +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV 1 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/jansson_private.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_PRIVATE_H +#define JANSSON_PRIVATE_H + +#include <stddef.h> +#include "jansson.h" +#include "hashtable.h" +#include "strbuffer.h" + +#define container_of(ptr_, type_, member_) \ + ((type_ *)((char *)ptr_ - offsetof(type_, member_))) + +/* On some platforms, max() may already be defined */ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* va_copy is a C99 feature. In C89 implementations, it's sometimes + available as __va_copy. If not, memcpy() should do the trick. */ +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) +#endif +#endif + +typedef struct { + json_t json; + hashtable_t hashtable; + size_t serial; + int visited; +} json_object_t; + +typedef struct { + json_t json; + size_t size; + size_t entries; + json_t **table; + int visited; +} json_array_t; + +typedef struct { + json_t json; + char *value; + size_t length; +} json_string_t; + +typedef struct { + json_t json; + double value; +} json_real_t; + +typedef struct { + json_t json; + json_int_t value; +} json_integer_t; + +#define json_to_object(json_) container_of(json_, json_object_t, json) +#define json_to_array(json_) container_of(json_, json_array_t, json) +#define json_to_string(json_) container_of(json_, json_string_t, json) +#define json_to_real(json_) container_of(json_, json_real_t, json) +#define json_to_integer(json_) container_of(json_, json_integer_t, json) + +/* Create a string by taking ownership of an existing buffer */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); + +/* Error message formatting */ +void jsonp_error_init(json_error_t *error, const char *source); +void jsonp_error_set_source(json_error_t *error, const char *source); +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, const char *msg, ...); +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, const char *msg, va_list ap); + +/* Locale independent string<->double conversions */ +int jsonp_strtod(strbuffer_t *strbuffer, double *out); +int jsonp_dtostr(char *buffer, size_t size, double value, int prec); + +/* Wrappers for custom memory functions */ +void* jsonp_malloc(size_t size); +void jsonp_free(void *ptr); +char *jsonp_strndup(const char *str, size_t length); +char *jsonp_strdup(const char *str); +char *jsonp_strndup(const char *str, size_t len); + + +/* Windows compatibility */ +#if defined(_WIN32) || defined(WIN32) +# if defined(_MSC_VER) /* MS compiller */ +# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */ +# define snprintf _snprintf +# endif +# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */ +# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) +# endif +# else /* Other Windows compiller, old definition */ +# define snprintf _snprintf +# define vsnprintf _vsnprintf +# endif +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/load.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "jansson.h" +#include "jansson_private.h" +#include "strbuffer.h" +#include "utf.h" + +#define STREAM_STATE_OK 0 +#define STREAM_STATE_EOF -1 +#define STREAM_STATE_ERROR -2 + +#define TOKEN_INVALID -1 +#define TOKEN_EOF 0 +#define TOKEN_STRING 256 +#define TOKEN_INTEGER 257 +#define TOKEN_REAL 258 +#define TOKEN_TRUE 259 +#define TOKEN_FALSE 260 +#define TOKEN_NULL 261 + +/* Locale independent versions of isxxx() functions */ +#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') +#define l_islower(c) ('a' <= (c) && (c) <= 'z') +#define l_isalpha(c) (l_isupper(c) || l_islower(c)) +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isxdigit(c) \ + (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) + +/* Read one byte from stream, convert to unsigned char, then int, and + return. return EOF on end of file. This corresponds to the + behaviour of fgetc(). */ +typedef int (*get_func)(void *data); + +typedef struct { + get_func get; + void *data; + char buffer[5]; + size_t buffer_pos; + int state; + int line; + int column, last_column; + size_t position; +} stream_t; + +typedef struct { + stream_t stream; + strbuffer_t saved_text; + int token; + union { + struct { + char *val; + size_t len; + } string; + json_int_t integer; + double real; + } value; +} lex_t; + +#define stream_to_lex(stream) container_of(stream, lex_t, stream) + + +/*** error reporting ***/ + +static void error_set(json_error_t *error, const lex_t *lex, + const char *msg, ...) +{ + va_list ap; + char msg_text[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH]; + + int line = -1, col = -1; + size_t pos = 0; + const char *result = msg_text; + + if(!error) + return; + + va_start(ap, msg); + vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); + msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + va_end(ap); + + if(lex) + { + const char *saved_text = strbuffer_value(&lex->saved_text); + + line = lex->stream.line; + col = lex->stream.column; + pos = lex->stream.position; + + if(saved_text && saved_text[0]) + { + if(lex->saved_text.length <= 20) { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + "%s near '%s'", msg_text, saved_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + else + { + if(lex->stream.state == STREAM_STATE_ERROR) { + /* No context for UTF-8 decoding errors */ + result = msg_text; + } + else { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + "%s near end of file", msg_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + } + + jsonp_error_set(error, line, col, pos, "%s", result); +} + + +/*** lexical analyzer ***/ + +static void +stream_init(stream_t *stream, get_func get, void *data) +{ + stream->get = get; + stream->data = data; + stream->buffer[0] = '\0'; + stream->buffer_pos = 0; + + stream->state = STREAM_STATE_OK; + stream->line = 1; + stream->column = 0; + stream->position = 0; +} + +static int stream_get(stream_t *stream, json_error_t *error) +{ + int c; + + if(stream->state != STREAM_STATE_OK) + return stream->state; + + if(!stream->buffer[stream->buffer_pos]) + { + c = stream->get(stream->data); + if(c == EOF) { + stream->state = STREAM_STATE_EOF; + return STREAM_STATE_EOF; + } + + stream->buffer[0] = c; + stream->buffer_pos = 0; + + if(0x80 <= c && c <= 0xFF) + { + /* multi-byte UTF-8 sequence */ + int i, count; + + count = utf8_check_first(c); + if(!count) + goto out; + + assert(count >= 2); + + for(i = 1; i < count; i++) + stream->buffer[i] = stream->get(stream->data); + + if(!utf8_check_full(stream->buffer, count, NULL)) + goto out; + + stream->buffer[count] = '\0'; + } + else + stream->buffer[1] = '\0'; + } + + c = stream->buffer[stream->buffer_pos++]; + + stream->position++; + if(c == '\n') { + stream->line++; + stream->last_column = stream->column; + stream->column = 0; + } + else if(utf8_check_first(c)) { + /* track the Unicode character column, so increment only if + this is the first character of a UTF-8 sequence */ + stream->column++; + } + + return c; + +out: + stream->state = STREAM_STATE_ERROR; + error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c); + return STREAM_STATE_ERROR; +} + +static void stream_unget(stream_t *stream, int c) +{ + if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR) + return; + + stream->position--; + if(c == '\n') { + stream->line--; + stream->column = stream->last_column; + } + else if(utf8_check_first(c)) + stream->column--; + + assert(stream->buffer_pos > 0); + stream->buffer_pos--; + assert(stream->buffer[stream->buffer_pos] == c); +} + + +static int lex_get(lex_t *lex, json_error_t *error) +{ + return stream_get(&lex->stream, error); +} + +static void lex_save(lex_t *lex, int c) +{ + strbuffer_append_byte(&lex->saved_text, c); +} + +static int lex_get_save(lex_t *lex, json_error_t *error) +{ + int c = stream_get(&lex->stream, error); + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) + lex_save(lex, c); + return c; +} + +static void lex_unget(lex_t *lex, int c) +{ + stream_unget(&lex->stream, c); +} + +static void lex_unget_unsave(lex_t *lex, int c) +{ + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { + /* Since we treat warnings as errors, when assertions are turned + * off the "d" variable would be set but never used. Which is + * treated as an error by GCC. + */ + #ifndef NDEBUG + char d; + #endif + stream_unget(&lex->stream, c); + #ifndef NDEBUG + d = + #endif + strbuffer_pop(&lex->saved_text); + assert(c == d); + } +} + +static void lex_save_cached(lex_t *lex) +{ + while(lex->stream.buffer[lex->stream.buffer_pos] != '\0') + { + lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]); + lex->stream.buffer_pos++; + lex->stream.position++; + } +} + +static void lex_free_string(lex_t *lex) +{ + jsonp_free(lex->value.string.val); + lex->value.string.val = NULL; + lex->value.string.len = 0; +} + +/* assumes that str points to 'u' plus at least 4 valid hex digits */ +static int32_t decode_unicode_escape(const char *str) +{ + int i; + int32_t value = 0; + + assert(str[0] == 'u'); + + for(i = 1; i <= 4; i++) { + char c = str[i]; + value <<= 4; + if(l_isdigit(c)) + value += c - '0'; + else if(l_islower(c)) + value += c - 'a' + 10; + else if(l_isupper(c)) + value += c - 'A' + 10; + else + return -1; + } + + return value; +} + +static void lex_scan_string(lex_t *lex, json_error_t *error) +{ + int c; + const char *p; + char *t; + int i; + + lex->value.string.val = NULL; + lex->token = TOKEN_INVALID; + + c = lex_get_save(lex, error); + + while(c != '"') { + if(c == STREAM_STATE_ERROR) + goto out; + + else if(c == STREAM_STATE_EOF) { + error_set(error, lex, "premature end of input"); + goto out; + } + + else if(0 <= c && c <= 0x1F) { + /* control character */ + lex_unget_unsave(lex, c); + if(c == '\n') + error_set(error, lex, "unexpected newline", c); + else + error_set(error, lex, "control character 0x%x", c); + goto out; + } + + else if(c == '\\') { + c = lex_get_save(lex, error); + if(c == 'u') { + c = lex_get_save(lex, error); + for(i = 0; i < 4; i++) { + if(!l_isxdigit(c)) { + error_set(error, lex, "invalid escape"); + goto out; + } + c = lex_get_save(lex, error); + } + } + else if(c == '"' || c == '\\' || c == '/' || c == 'b' || + c == 'f' || c == 'n' || c == 'r' || c == 't') + c = lex_get_save(lex, error); + else { + error_set(error, lex, "invalid escape"); + goto out; + } + } + else + c = lex_get_save(lex, error); + } + + /* the actual value is at most of the same length as the source + string, because: + - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte + - a single \uXXXX escape (length 6) is converted to at most 3 bytes + - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair + are converted to 4 bytes + */ + t = jsonp_malloc(lex->saved_text.length + 1); + if(!t) { + /* this is not very nice, since TOKEN_INVALID is returned */ + goto out; + } + lex->value.string.val = t; + + /* + 1 to skip the " */ + p = strbuffer_value(&lex->saved_text) + 1; + + while(*p != '"') { + if(*p == '\\') { + p++; + if(*p == 'u') { + size_t length; + int32_t value; + + value = decode_unicode_escape(p); + if(value < 0) { + error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if(0xD800 <= value && value <= 0xDBFF) { + /* surrogate pair */ + if(*p == '\\' && *(p + 1) == 'u') { + int32_t value2 = decode_unicode_escape(++p); + if(value2 < 0) { + error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if(0xDC00 <= value2 && value2 <= 0xDFFF) { + /* valid second surrogate */ + value = + ((value - 0xD800) << 10) + + (value2 - 0xDC00) + + 0x10000; + } + else { + /* invalid second surrogate */ + error_set(error, lex, + "invalid Unicode '\\u%04X\\u%04X'", + value, value2); + goto out; + } + } + else { + /* no second surrogate */ + error_set(error, lex, "invalid Unicode '\\u%04X'", + value); + goto out; + } + } + else if(0xDC00 <= value && value <= 0xDFFF) { + error_set(error, lex, "invalid Unicode '\\u%04X'", value); + goto out; + } + + if(utf8_encode(value, t, &length)) + assert(0); + t += length; + } + else { + switch(*p) { + case '"': case '\\': case '/': + *t = *p; break; + case 'b': *t = '\b'; break; + case 'f': *t = '\f'; break; + case 'n': *t = '\n'; break; + case 'r': *t = '\r'; break; + case 't': *t = '\t'; break; + default: assert(0); + } + t++; + p++; + } + } + else + *(t++) = *(p++); + } + *t = '\0'; + lex->value.string.len = t - lex->value.string.val; + lex->token = TOKEN_STRING; + return; + +out: + lex_free_string(lex); +} + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _MSC_VER /* Microsoft Visual Studio */ +#define json_strtoint _strtoi64 +#else +#define json_strtoint strtoll +#endif +#else +#define json_strtoint strtol +#endif +#endif + +static int lex_scan_number(lex_t *lex, int c, json_error_t *error) +{ + const char *saved_text; + char *end; + double doubleval; + + lex->token = TOKEN_INVALID; + + if(c == '-') + c = lex_get_save(lex, error); + + if(c == '0') { + c = lex_get_save(lex, error); + if(l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + } + else if(l_isdigit(c)) { + c = lex_get_save(lex, error); + while(l_isdigit(c)) + c = lex_get_save(lex, error); + } + else { + lex_unget_unsave(lex, c); + goto out; + } + + if(c != '.' && c != 'E' && c != 'e') { + json_int_t intval; + + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + errno = 0; + intval = json_strtoint(saved_text, &end, 10); + if(errno == ERANGE) { + if(intval < 0) + error_set(error, lex, "too big negative integer"); + else + error_set(error, lex, "too big integer"); + goto out; + } + + assert(end == saved_text + lex->saved_text.length); + + lex->token = TOKEN_INTEGER; + lex->value.integer = intval; + return 0; + } + + if(c == '.') { + c = lex_get(lex, error); + if(!l_isdigit(c)) { + lex_unget(lex, c); + goto out; + } + lex_save(lex, c); + + c = lex_get_save(lex, error); + while(l_isdigit(c)) + c = lex_get_save(lex, error); + } + + if(c == 'E' || c == 'e') { + c = lex_get_save(lex, error); + if(c == '+' || c == '-') + c = lex_get_save(lex, error); + + if(!l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + + c = lex_get_save(lex, error); + while(l_isdigit(c)) + c = lex_get_save(lex, error); + } + + lex_unget_unsave(lex, c); + + if(jsonp_strtod(&lex->saved_text, &doubleval)) { + error_set(error, lex, "real number overflow"); + goto out; + } + + lex->token = TOKEN_REAL; + lex->value.real = doubleval; + return 0; + +out: + return -1; +} + +static int lex_scan(lex_t *lex, json_error_t *error) +{ + int c; + + strbuffer_clear(&lex->saved_text); + + if(lex->token == TOKEN_STRING) + lex_free_string(lex); + + c = lex_get(lex, error); + while(c == ' ' || c == '\t' || c == '\n' || c == '\r') + c = lex_get(lex, error); + + if(c == STREAM_STATE_EOF) { + lex->token = TOKEN_EOF; + goto out; + } + + if(c == STREAM_STATE_ERROR) { + lex->token = TOKEN_INVALID; + goto out; + } + + lex_save(lex, c); + + if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') + lex->token = c; + + else if(c == '"') + lex_scan_string(lex, error); + + else if(l_isdigit(c) || c == '-') { + if(lex_scan_number(lex, c, error)) + goto out; + } + + else if(l_isalpha(c)) { + /* eat up the whole identifier for clearer error messages */ + const char *saved_text; + + c = lex_get_save(lex, error); + while(l_isalpha(c)) + c = lex_get_save(lex, error); + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + if(strcmp(saved_text, "true") == 0) + lex->token = TOKEN_TRUE; + else if(strcmp(saved_text, "false") == 0) + lex->token = TOKEN_FALSE; + else if(strcmp(saved_text, "null") == 0) + lex->token = TOKEN_NULL; + else + lex->token = TOKEN_INVALID; + } + + else { + /* save the rest of the input UTF-8 sequence to get an error + message of valid UTF-8 */ + lex_save_cached(lex); + lex->token = TOKEN_INVALID; + } + +out: + return lex->token; +} + +static char *lex_steal_string(lex_t *lex, size_t *out_len) +{ + char *result = NULL; + if(lex->token == TOKEN_STRING) { + result = lex->value.string.val; + *out_len = lex->value.string.len; + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + return result; +} + +static int lex_init(lex_t *lex, get_func get, void *data) +{ + stream_init(&lex->stream, get, data); + if(strbuffer_init(&lex->saved_text)) + return -1; + + lex->token = TOKEN_INVALID; + return 0; +} + +static void lex_close(lex_t *lex) +{ + if(lex->token == TOKEN_STRING) + lex_free_string(lex); + strbuffer_close(&lex->saved_text); +} + + +/*** parser ***/ + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error); + +static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *object = json_object(); + if(!object) + return NULL; + + lex_scan(lex, error); + if(lex->token == '}') + return object; + + while(1) { + char *key; + size_t len; + json_t *value; + + if(lex->token != TOKEN_STRING) { + error_set(error, lex, "string or '}' expected"); + goto error; + } + + key = lex_steal_string(lex, &len); + if(!key) + return NULL; + if (memchr(key, '\0', len)) { + jsonp_free(key); + error_set(error, lex, "NUL byte in object key not supported"); + goto error; + } + + if(flags & JSON_REJECT_DUPLICATES) { + if(json_object_get(object, key)) { + jsonp_free(key); + error_set(error, lex, "duplicate object key"); + goto error; + } + } + + lex_scan(lex, error); + if(lex->token != ':') { + jsonp_free(key); + error_set(error, lex, "':' expected"); + goto error; + } + + lex_scan(lex, error); + value = parse_value(lex, flags, error); + if(!value) { + jsonp_free(key); + goto error; + } + + if(json_object_set_nocheck(object, key, value)) { + jsonp_free(key); + json_decref(value); + goto error; + } + + json_decref(value); + jsonp_free(key); + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != '}') { + error_set(error, lex, "'}' expected"); + goto error; + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *array = json_array(); + if(!array) + return NULL; + + lex_scan(lex, error); + if(lex->token == ']') + return array; + + while(lex->token) { + json_t *elem = parse_value(lex, flags, error); + if(!elem) + goto error; + + if(json_array_append(array, elem)) { + json_decref(elem); + goto error; + } + json_decref(elem); + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != ']') { + error_set(error, lex, "']' expected"); + goto error; + } + + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *json; + double value; + + switch(lex->token) { + case TOKEN_STRING: { + const char *value = lex->value.string.val; + size_t len = lex->value.string.len; + + if(!(flags & JSON_ALLOW_NUL)) { + if(memchr(value, '\0', len)) { + error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL"); + return NULL; + } + } + + json = jsonp_stringn_nocheck_own(value, len); + if(json) { + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + break; + } + + case TOKEN_INTEGER: { + if (flags & JSON_DECODE_INT_AS_REAL) { + if(jsonp_strtod(&lex->saved_text, &value)) { + error_set(error, lex, "real number overflow"); + return NULL; + } + json = json_real(value); + } else { + json = json_integer(lex->value.integer); + } + break; + } + + case TOKEN_REAL: { + json = json_real(lex->value.real); + break; + } + + case TOKEN_TRUE: + json = json_true(); + break; + + case TOKEN_FALSE: + json = json_false(); + break; + + case TOKEN_NULL: + json = json_null(); + break; + + case '{': + json = parse_object(lex, flags, error); + break; + + case '[': + json = parse_array(lex, flags, error); + break; + + case TOKEN_INVALID: + error_set(error, lex, "invalid token"); + return NULL; + + default: + error_set(error, lex, "unexpected token"); + return NULL; + } + + if(!json) + return NULL; + + return json; +} + +static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *result; + + lex_scan(lex, error); + if(!(flags & JSON_DECODE_ANY)) { + if(lex->token != '[' && lex->token != '{') { + error_set(error, lex, "'[' or '{' expected"); + return NULL; + } + } + + result = parse_value(lex, flags, error); + if(!result) + return NULL; + + if(!(flags & JSON_DISABLE_EOF_CHECK)) { + lex_scan(lex, error); + if(lex->token != TOKEN_EOF) { + error_set(error, lex, "end of file expected"); + json_decref(result); + return NULL; + } + } + + if(error) { + /* Save the position even though there was no error */ + error->position = lex->stream.position; + } + + return result; +} + +typedef struct +{ + const char *data; + int pos; +} string_data_t; + +static int string_get(void *data) +{ + char c; + string_data_t *stream = (string_data_t *)data; + c = stream->data[stream->pos]; + if(c == '\0') + return EOF; + else + { + stream->pos++; + return (unsigned char)c; + } +} + +json_t *json_loads(const char *string, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + string_data_t stream_data; + + jsonp_error_init(error, "<string>"); + + if (string == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + stream_data.data = string; + stream_data.pos = 0; + + if(lex_init(&lex, string_get, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +typedef struct +{ + const char *data; + size_t len; + size_t pos; +} buffer_data_t; + +static int buffer_get(void *data) +{ + char c; + buffer_data_t *stream = data; + if(stream->pos >= stream->len) + return EOF; + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + buffer_data_t stream_data; + + jsonp_error_init(error, "<buffer>"); + + if (buffer == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + stream_data.data = buffer; + stream_data.pos = 0; + stream_data.len = buflen; + + if(lex_init(&lex, buffer_get, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) +{ + lex_t lex; + const char *source; + json_t *result; + + if(input == stdin) + source = "<stdin>"; + else + source = "<stream>"; + + jsonp_error_init(error, source); + + if (input == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fgetc, input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) +{ + json_t *result; + FILE *fp; + + jsonp_error_init(error, path); + + if (path == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + fp = fopen(path, "rb"); + if(!fp) + { + error_set(error, NULL, "unable to open %s: %s", + path, strerror(errno)); + return NULL; + } + + result = json_loadf(fp, flags, error); + + fclose(fp); + return result; +} + +#define MAX_BUF_LEN 1024 + +typedef struct +{ + char data[MAX_BUF_LEN]; + size_t len; + size_t pos; + json_load_callback_t callback; + void *arg; +} callback_data_t; + +static int callback_get(void *data) +{ + char c; + callback_data_t *stream = data; + + if(stream->pos >= stream->len) { + stream->pos = 0; + stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); + if(stream->len == 0 || stream->len == (size_t)-1) + return EOF; + } + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + + callback_data_t stream_data; + + memset(&stream_data, 0, sizeof(stream_data)); + stream_data.callback = callback; + stream_data.arg = arg; + + jsonp_error_init(error, "<callback>"); + + if (callback == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)callback_get, &stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/lookup3.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,381 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +#include <stdlib.h> + +#ifdef HAVE_CONFIG_H +#include <jansson_private_config.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> /* defines uint32_t etc */ +#endif + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> /* attempt to define endianness */ +#endif + +#ifdef HAVE_ENDIAN_H +# include <endian.h> /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h); + +By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial. It's free. + +Use for hash table lookup, or anything where one collision in 2^^32 is +acceptable. Do NOT use for cryptographic purposes. +------------------------------------------------------------------------------- +*/ + +static uint32_t hashlittle(const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + +/* Detect Valgrind or AddressSanitizer */ +#ifdef VALGRIND +# define NO_MASKING_TRICK 1 +#else +# if defined(__has_feature) /* Clang */ +# if __has_feature(address_sanitizer) /* is ASAN enabled? */ +# define NO_MASKING_TRICK 1 +# endif +# else +# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */ +# define NO_MASKING_TRICK 1 +# endif +# endif +#endif + +#ifdef NO_MASKING_TRICK + const uint8_t *k8; +#endif + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef NO_MASKING_TRICK + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + 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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/memory.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net> + * + * Jansson is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include <stdlib.h> +#include <string.h> + +#include "jansson.h" +#include "jansson_private.h" + +/* C89 allows these to be macros */ +#undef malloc +#undef free + +/* memory function pointers */ +static json_malloc_t do_malloc = malloc; +static json_free_t do_free = free; + +void *jsonp_malloc(size_t size) +{ + if(!size) + return NULL; + + return (*do_malloc)(size); +} + +void jsonp_free(void *ptr) +{ + if(!ptr) + return; + + (*do_free)(ptr); +} + +char *jsonp_strdup(const char *str) +{ + return jsonp_strndup(str, strlen(str)); +} + +char *jsonp_strndup(const char *str, size_t len) +{ + char *new_str; + + new_str = jsonp_malloc(len + 1); + if(!new_str) + return NULL; + + memcpy(new_str, str, len); + new_str[len] = '\0'; + return new_str; +} + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) +{ + do_malloc = malloc_fn; + do_free = free_fn; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/pack_unpack.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,805 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <string.h> +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" + +typedef struct { + int line; + int column; + size_t pos; + char token; +} token_t; + +typedef struct { + const char *start; + const char *fmt; + token_t prev_token; + token_t token; + token_t next_token; + json_error_t *error; + size_t flags; + int line; + int column; + size_t pos; +} scanner_t; + +#define token(scanner) ((scanner)->token.token) + +static const char * const type_names[] = { + "object", + "array", + "string", + "integer", + "real", + "true", + "false", + "null" +}; + +#define type_name(x) type_names[json_typeof(x)] + +static const char unpack_value_starters[] = "{[siIbfFOon"; + + +static void scanner_init(scanner_t *s, json_error_t *error, + size_t flags, const char *fmt) +{ + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + memset(&s->prev_token, 0, sizeof(token_t)); + memset(&s->token, 0, sizeof(token_t)); + memset(&s->next_token, 0, sizeof(token_t)); + s->line = 1; + s->column = 0; + s->pos = 0; +} + +static void next_token(scanner_t *s) +{ + const char *t; + s->prev_token = s->token; + + if(s->next_token.line) { + s->token = s->next_token; + s->next_token.line = 0; + return; + } + + t = s->fmt; + s->column++; + s->pos++; + + /* skip space and ignored chars */ + while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { + if(*t == '\n') { + s->line++; + s->column = 1; + } + else + s->column++; + + s->pos++; + t++; + } + + s->token.token = *t; + s->token.line = s->line; + s->token.column = s->column; + s->token.pos = s->pos; + + t++; + s->fmt = t; +} + +static void prev_token(scanner_t *s) +{ + s->next_token = s->token; + s->token = s->prev_token; +} + +static void set_error(scanner_t *s, const char *source, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, + fmt, ap); + + jsonp_error_set_source(s->error, source); + + va_end(ap); +} + +static json_t *pack(scanner_t *s, va_list *ap); + + +/* ours will be set to 1 if jsonp_free() must be called for the result + afterwards */ +static char *read_string(scanner_t *s, va_list *ap, + const char *purpose, size_t *out_len, int *ours) +{ + char t; + strbuffer_t strbuff; + const char *str; + size_t length; + + next_token(s); + t = token(s); + prev_token(s); + + if(t != '#' && t != '%' && t != '+') { + /* Optimize the simple case */ + str = va_arg(*ap, const char *); + + if(!str) { + set_error(s, "<args>", "NULL string argument"); + return NULL; + } + + length = strlen(str); + + if(!utf8_check_string(str, length)) { + set_error(s, "<args>", "Invalid UTF-8 %s", purpose); + return NULL; + } + + *out_len = length; + *ours = 0; + return (char *)str; + } + + strbuffer_init(&strbuff); + + while(1) { + str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "<args>", "NULL string argument"); + strbuffer_close(&strbuff); + return NULL; + } + + next_token(s); + + if(token(s) == '#') { + length = va_arg(*ap, int); + } + else if(token(s) == '%') { + length = va_arg(*ap, size_t); + } + else { + prev_token(s); + length = strlen(str); + } + + if(strbuffer_append_bytes(&strbuff, str, length) == -1) { + set_error(s, "<internal>", "Out of memory"); + strbuffer_close(&strbuff); + return NULL; + } + + next_token(s); + if(token(s) != '+') { + prev_token(s); + break; + } + } + + if(!utf8_check_string(strbuff.value, strbuff.length)) { + set_error(s, "<args>", "Invalid UTF-8 %s", purpose); + strbuffer_close(&strbuff); + return NULL; + } + + *out_len = strbuff.length; + *ours = 1; + return strbuffer_steal_value(&strbuff); +} + +static json_t *pack_object(scanner_t *s, va_list *ap) +{ + json_t *object = json_object(); + next_token(s); + + while(token(s) != '}') { + char *key; + size_t len; + int ours; + json_t *value; + + if(!token(s)) { + set_error(s, "<format>", "Unexpected end of format string"); + goto error; + } + + if(token(s) != 's') { + set_error(s, "<format>", "Expected format 's', got '%c'", token(s)); + goto error; + } + + key = read_string(s, ap, "object key", &len, &ours); + if(!key) + goto error; + + next_token(s); + + value = pack(s, ap); + if(!value) { + if(ours) + jsonp_free(key); + + goto error; + } + + if(json_object_set_new_nocheck(object, key, value)) { + if(ours) + jsonp_free(key); + + set_error(s, "<internal>", "Unable to add key \"%s\"", key); + goto error; + } + + if(ours) + jsonp_free(key); + + next_token(s); + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *pack_array(scanner_t *s, va_list *ap) +{ + json_t *array = json_array(); + next_token(s); + + while(token(s) != ']') { + json_t *value; + + if(!token(s)) { + set_error(s, "<format>", "Unexpected end of format string"); + goto error; + } + + value = pack(s, ap); + if(!value) + goto error; + + if(json_array_append_new(array, value)) { + set_error(s, "<internal>", "Unable to append to array"); + goto error; + } + + next_token(s); + } + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *pack(scanner_t *s, va_list *ap) +{ + switch(token(s)) { + case '{': + return pack_object(s, ap); + + case '[': + 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); + } + + case 'n': /* null */ + return json_null(); + + case 'b': /* boolean */ + return va_arg(*ap, int) ? json_true() : json_false(); + + case 'i': /* integer from int */ + return json_integer(va_arg(*ap, int)); + + case 'I': /* integer from json_int_t */ + return json_integer(va_arg(*ap, json_int_t)); + + case 'f': /* real */ + return json_real(va_arg(*ap, double)); + + case 'O': /* a json_t object; increments refcount */ + return json_incref(va_arg(*ap, json_t *)); + + case 'o': /* a json_t object; doesn't increment refcount */ + return va_arg(*ap, json_t *); + + default: + set_error(s, "<format>", "Unexpected format character '%c'", + token(s)); + return NULL; + } +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap); + +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 + were accessed is not enough, as the same key can be unpacked + multiple times. + */ + hashtable_t key_set; + + if(hashtable_init(&key_set)) { + set_error(s, "<internal>", "Out of memory"); + return -1; + } + + if(root && !json_is_object(root)) { + set_error(s, "<validation>", "Expected object, got %s", + type_name(root)); + goto out; + } + next_token(s); + + while(token(s) != '}') { + const char *key; + json_t *value; + int opt = 0; + + if(strict != 0) { + set_error(s, "<format>", "Expected '}' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), token(s)); + goto out; + } + + if(!token(s)) { + set_error(s, "<format>", "Unexpected end of format string"); + goto out; + } + + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(token(s) != 's') { + set_error(s, "<format>", "Expected format 's', got '%c'", token(s)); + goto out; + } + + key = va_arg(*ap, const char *); + if(!key) { + set_error(s, "<args>", "NULL object key"); + goto out; + } + + next_token(s); + + if(token(s) == '?') { + opt = gotopt = 1; + next_token(s); + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_object_get(root, key); + if(!value && !opt) { + set_error(s, "<validation>", "Object item not found: %s", key); + goto out; + } + } + + if(unpack(s, value, ap)) + goto out; + + hashtable_set(&key_set, key, 0, json_null()); + next_token(s); + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1) { + /* We need to check that all non optional items have been parsed */ + const char *key; + 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++; + } + } + } else { + /* No optional keys, we can just compare the number of items */ + unpacked = (long)json_object_size(root) - (long)key_set.size; + } + if (unpacked) { + set_error(s, "<validation>", "%li object item(s) left unpacked", unpacked); + goto out; + } + } + + ret = 0; + +out: + hashtable_close(&key_set); + return ret; +} + +static int unpack_array(scanner_t *s, json_t *root, va_list *ap) +{ + size_t i = 0; + int strict = 0; + + if(root && !json_is_array(root)) { + set_error(s, "<validation>", "Expected array, got %s", type_name(root)); + return -1; + } + next_token(s); + + while(token(s) != ']') { + json_t *value; + + if(strict != 0) { + set_error(s, "<format>", "Expected ']' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), + token(s)); + return -1; + } + + if(!token(s)) { + set_error(s, "<format>", "Unexpected end of format string"); + return -1; + } + + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(!strchr(unpack_value_starters, token(s))) { + set_error(s, "<format>", "Unexpected format character '%c'", + token(s)); + return -1; + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_array_get(root, i); + if(!value) { + set_error(s, "<validation>", "Array index %lu out of range", + (unsigned long)i); + return -1; + } + } + + if(unpack(s, value, ap)) + return -1; + + next_token(s); + i++; + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1 && i != json_array_size(root)) { + long diff = (long)json_array_size(root) - (long)i; + set_error(s, "<validation>", "%li array item(s) left unpacked", diff); + return -1; + } + + return 0; +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap) +{ + switch(token(s)) + { + case '{': + return unpack_object(s, root, ap); + + case '[': + return unpack_array(s, root, ap); + + case 's': + if(root && !json_is_string(root)) { + set_error(s, "<validation>", "Expected string, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + const char **str_target; + size_t *len_target = NULL; + + str_target = va_arg(*ap, const char **); + if(!str_target) { + set_error(s, "<args>", "NULL string argument"); + return -1; + } + + next_token(s); + + if(token(s) == '%') { + len_target = va_arg(*ap, size_t *); + if(!len_target) { + set_error(s, "<args>", "NULL string length argument"); + return -1; + } + } + else + prev_token(s); + + if(root) { + *str_target = json_string_value(root); + if(len_target) + *len_target = json_string_length(root); + } + } + return 0; + + case 'i': + if(root && !json_is_integer(root)) { + set_error(s, "<validation>", "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = (int)json_integer_value(root); + } + + return 0; + + case 'I': + if(root && !json_is_integer(root)) { + set_error(s, "<validation>", "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_int_t *target = va_arg(*ap, json_int_t*); + if(root) + *target = json_integer_value(root); + } + + return 0; + + case 'b': + if(root && !json_is_boolean(root)) { + set_error(s, "<validation>", "Expected true or false, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = json_is_true(root); + } + + return 0; + + case 'f': + if(root && !json_is_real(root)) { + set_error(s, "<validation>", "Expected real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_real_value(root); + } + + return 0; + + case 'F': + if(root && !json_is_number(root)) { + set_error(s, "<validation>", "Expected real or integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_number_value(root); + } + + return 0; + + case 'O': + if(root && !(s->flags & JSON_VALIDATE_ONLY)) + json_incref(root); + /* Fall through */ + + case 'o': + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_t **target = va_arg(*ap, json_t**); + if(root) + *target = root; + } + + return 0; + + case 'n': + /* Never assign, just validate */ + if(root && !json_is_null(root)) { + set_error(s, "<validation>", "Expected null, got %s", + type_name(root)); + return -1; + } + return 0; + + default: + set_error(s, "<format>", "Unexpected format character '%c'", + token(s)); + return -1; + } +} + +json_t *json_vpack_ex(json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + json_t *value; + + if(!fmt || !*fmt) { + jsonp_error_init(error, "<format>"); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return NULL; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + value = pack(&s, &ap_copy); + va_end(ap_copy); + + if(!value) + return NULL; + + next_token(&s); + if(token(&s)) { + json_decref(value); + set_error(&s, "<format>", "Garbage after format string"); + return NULL; + } + + return value; +} + +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(error, flags, fmt, ap); + va_end(ap); + + return value; +} + +json_t *json_pack(const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(NULL, 0, fmt, ap); + va_end(ap); + + return value; +} + +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + + if(!root) { + jsonp_error_init(error, "<root>"); + jsonp_error_set(error, -1, -1, 0, "NULL root value"); + return -1; + } + + if(!fmt || !*fmt) { + jsonp_error_init(error, "<format>"); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + if(unpack(&s, root, &ap_copy)) { + va_end(ap_copy); + return -1; + } + va_end(ap_copy); + + next_token(&s); + if(token(&s)) { + set_error(&s, "<format>", "Garbage after format string"); + return -1; + } + + return 0; +} + +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, error, flags, fmt, ap); + va_end(ap); + + return ret; +} + +int json_unpack(json_t *root, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, NULL, 0, fmt, ap); + va_end(ap); + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/strbuffer.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <string.h> +#include "jansson_private.h" +#include "strbuffer.h" + +#define STRBUFFER_MIN_SIZE 16 +#define STRBUFFER_FACTOR 2 +#define STRBUFFER_SIZE_MAX ((size_t)-1) + +int strbuffer_init(strbuffer_t *strbuff) +{ + strbuff->size = STRBUFFER_MIN_SIZE; + strbuff->length = 0; + + strbuff->value = jsonp_malloc(strbuff->size); + if(!strbuff->value) + return -1; + + /* initialize to empty */ + strbuff->value[0] = '\0'; + return 0; +} + +void strbuffer_close(strbuffer_t *strbuff) +{ + if(strbuff->value) + jsonp_free(strbuff->value); + + strbuff->size = 0; + strbuff->length = 0; + strbuff->value = NULL; +} + +void strbuffer_clear(strbuffer_t *strbuff) +{ + strbuff->length = 0; + strbuff->value[0] = '\0'; +} + +const char *strbuffer_value(const strbuffer_t *strbuff) +{ + return strbuff->value; +} + +char *strbuffer_steal_value(strbuffer_t *strbuff) +{ + char *result = strbuff->value; + strbuff->value = NULL; + return result; +} + +int strbuffer_append(strbuffer_t *strbuff, const char *string) +{ + return strbuffer_append_bytes(strbuff, string, strlen(string)); +} + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte) +{ + return strbuffer_append_bytes(strbuff, &byte, 1); +} + +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) +{ + if(size >= strbuff->size - strbuff->length) + { + size_t new_size; + char *new_value; + + /* avoid integer overflow */ + if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR + || size > STRBUFFER_SIZE_MAX - 1 + || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) + return -1; + + new_size = max(strbuff->size * STRBUFFER_FACTOR, + strbuff->length + size + 1); + + new_value = jsonp_malloc(new_size); + if(!new_value) + return -1; + + memcpy(new_value, strbuff->value, strbuff->length); + + jsonp_free(strbuff->value); + strbuff->value = new_value; + strbuff->size = new_size; + } + + memcpy(strbuff->value + strbuff->length, data, size); + strbuff->length += size; + strbuff->value[strbuff->length] = '\0'; + + return 0; +} + +char strbuffer_pop(strbuffer_t *strbuff) +{ + if(strbuff->length > 0) { + char c = strbuff->value[--strbuff->length]; + strbuff->value[strbuff->length] = '\0'; + return c; + } + else + return '\0'; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/strbuffer.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef STRBUFFER_H +#define STRBUFFER_H + +typedef struct { + char *value; + size_t length; /* bytes used */ + size_t size; /* bytes allocated */ +} strbuffer_t; + +int strbuffer_init(strbuffer_t *strbuff); +void strbuffer_close(strbuffer_t *strbuff); + +void strbuffer_clear(strbuffer_t *strbuff); + +const char *strbuffer_value(const strbuffer_t *strbuff); + +/* Steal the value and close the strbuffer */ +char *strbuffer_steal_value(strbuffer_t *strbuff); + +int strbuffer_append(strbuffer_t *strbuff, const char *string); +int strbuffer_append_byte(strbuffer_t *strbuff, char byte); +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); + +char strbuffer_pop(strbuffer_t *strbuff); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/strconv.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,138 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "jansson_private.h" +#include "strbuffer.h" + +/* need jansson_private_config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include <jansson_private_config.h> +#endif + +#if JSON_HAVE_LOCALECONV +#include <locale.h> + +/* + - This code assumes that the decimal separator is exactly one + character. + + - If setlocale() is called by another thread between the call to + localeconv() and the call to sprintf() or strtod(), the result may + be wrong. setlocale() is not thread-safe and should not be used + this way. Multi-threaded programs should use uselocale() instead. +*/ + +static void to_locale(strbuffer_t *strbuffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(strbuffer->value, '.'); + if(pos) + *pos = *point; +} + +static void from_locale(char *buffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(buffer, *point); + if(pos) + *pos = '.'; +} +#endif + +int jsonp_strtod(strbuffer_t *strbuffer, double *out) +{ + double value; + char *end; + +#if JSON_HAVE_LOCALECONV + to_locale(strbuffer); +#endif + + errno = 0; + value = strtod(strbuffer->value, &end); + assert(end == strbuffer->value + strbuffer->length); + + if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) { + /* Overflow */ + return -1; + } + + *out = value; + return 0; +} + +int jsonp_dtostr(char *buffer, size_t size, double value, int precision) +{ + int ret; + char *start, *end; + size_t length; + + if (precision == 0) + precision = 17; + + ret = snprintf(buffer, size, "%.*g", precision, value); + if(ret < 0) + return -1; + + length = (size_t)ret; + if(length >= size) + return -1; + +#if JSON_HAVE_LOCALECONV + from_locale(buffer); +#endif + + /* Make sure there's a dot or 'e' in the output. Otherwise + a real is converted to an integer when decoding */ + if(strchr(buffer, '.') == NULL && + strchr(buffer, 'e') == NULL) + { + if(length + 3 >= size) { + /* No space to append ".0" */ + return -1; + } + buffer[length] = '.'; + buffer[length + 1] = '0'; + buffer[length + 2] = '\0'; + length += 2; + } + + /* Remove leading '+' from positive exponent. Also remove leading + zeros from exponents (added by some printf() implementations) */ + start = strchr(buffer, 'e'); + if(start) { + start++; + end = start + 1; + + if(*start == '-') + start++; + + while(*end == '0') + end++; + + if(end != start) { + memmove(start, end, length - (size_t)(end - buffer)); + length -= (size_t)(end - start); + } + } + + return (int)length; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/utf.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <string.h> +#include "utf.h" + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size) +{ + if(codepoint < 0) + return -1; + else if(codepoint < 0x80) + { + buffer[0] = (char)codepoint; + *size = 1; + } + else if(codepoint < 0x800) + { + buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); + buffer[1] = 0x80 + ((codepoint & 0x03F)); + *size = 2; + } + else if(codepoint < 0x10000) + { + buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); + buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); + buffer[2] = 0x80 + ((codepoint & 0x003F)); + *size = 3; + } + else if(codepoint <= 0x10FFFF) + { + buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); + buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); + buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); + buffer[3] = 0x80 + ((codepoint & 0x00003F)); + *size = 4; + } + else + return -1; + + return 0; +} + +size_t utf8_check_first(char byte) +{ + unsigned char u = (unsigned char)byte; + + if(u < 0x80) + return 1; + + if(0x80 <= u && u <= 0xBF) { + /* second, third or fourth byte of a multi-byte + sequence, i.e. a "continuation byte" */ + return 0; + } + else if(u == 0xC0 || u == 0xC1) { + /* overlong encoding of an ASCII byte */ + return 0; + } + else if(0xC2 <= u && u <= 0xDF) { + /* 2-byte sequence */ + return 2; + } + + else if(0xE0 <= u && u <= 0xEF) { + /* 3-byte sequence */ + return 3; + } + else if(0xF0 <= u && u <= 0xF4) { + /* 4-byte sequence */ + return 4; + } + else { /* u >= 0xF5 */ + /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid + UTF-8 */ + return 0; + } +} + +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint) +{ + size_t i; + int32_t value = 0; + unsigned char u = (unsigned char)buffer[0]; + + if(size == 2) + { + value = u & 0x1F; + } + else if(size == 3) + { + value = u & 0xF; + } + else if(size == 4) + { + value = u & 0x7; + } + else + return 0; + + for(i = 1; i < size; i++) + { + u = (unsigned char)buffer[i]; + + if(u < 0x80 || u > 0xBF) { + /* not a continuation byte */ + return 0; + } + + value = (value << 6) + (u & 0x3F); + } + + if(value > 0x10FFFF) { + /* not in Unicode range */ + return 0; + } + + else if(0xD800 <= value && value <= 0xDFFF) { + /* invalid code point (UTF-16 surrogate halves) */ + return 0; + } + + else if((size == 2 && value < 0x80) || + (size == 3 && value < 0x800) || + (size == 4 && value < 0x10000)) { + /* overlong encoding */ + return 0; + } + + if(codepoint) + *codepoint = value; + + return 1; +} + +const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint) +{ + size_t count; + int32_t value; + + if(!bufsize) + return buffer; + + count = utf8_check_first(buffer[0]); + if(count <= 0) + return NULL; + + if(count == 1) + value = (unsigned char)buffer[0]; + else + { + if(count > bufsize || !utf8_check_full(buffer, count, &value)) + return NULL; + } + + if(codepoint) + *codepoint = value; + + return buffer + count; +} + +int utf8_check_string(const char *string, size_t length) +{ + size_t i; + + for(i = 0; i < length; i++) + { + size_t count = utf8_check_first(string[i]); + if(count == 0) + return 0; + else if(count > 1) + { + if(count > length - i) + return 0; + + if(!utf8_check_full(&string[i], count, NULL)) + return 0; + + i += count - 1; + } + } + + return 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/utf.h Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UTF_H +#define UTF_H + +#ifdef HAVE_CONFIG_H +#include <jansson_private_config.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size); + +size_t utf8_check_first(char byte); +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint); +const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint); + +int utf8_check_string(const char *string, size_t length); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/jansson/src/value.c Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,1041 @@ +/* + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef HAVE_CONFIG_H +#include <jansson_private_config.h> +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include "jansson.h" +#include "hashtable.h" +#include "jansson_private.h" +#include "utf.h" + +/* Work around nonstandard isnan() and isinf() implementations */ +#ifndef isnan +#ifndef __sun +static JSON_INLINE int isnan(double x) { return x != x; } +#endif +#endif +#ifndef isinf +static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif + +static JSON_INLINE void json_init(json_t *json, json_type type) +{ + json->type = type; + json->refcount = 1; +} + + +/*** 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)) + { + jsonp_free(object); + return NULL; + } + + object->serial = 0; + object->visited = 0; + + return &object->json; +} + +static void json_delete_object(json_object_t *object) +{ + hashtable_close(&object->hashtable); + jsonp_free(object); +} + +size_t json_object_size(const json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return 0; + + object = json_to_object(json); + return object->hashtable.size; +} + +json_t *json_object_get(const json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_get(&object->hashtable, key); +} + +int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) +{ + json_object_t *object; + + if(!value) + return -1; + + if(!key || !json_is_object(json) || json == value) + { + json_decref(value); + return -1; + } + object = json_to_object(json); + + if(hashtable_set(&object->hashtable, key, object->serial++, value)) + { + json_decref(value); + return -1; + } + + return 0; +} + +int json_object_set_new(json_t *json, const char *key, json_t *value) +{ + if(!key || !utf8_check_string(key, strlen(key))) + { + json_decref(value); + return -1; + } + + return json_object_set_new_nocheck(json, key, value); +} + +int json_object_del(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return -1; + + object = json_to_object(json); + return hashtable_del(&object->hashtable, key); +} + +int json_object_clear(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return -1; + + object = json_to_object(json); + + hashtable_clear(&object->hashtable); + object->serial = 0; + + return 0; +} + +int json_object_update(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_set_nocheck(object, key, value)) + return -1; + } + + return 0; +} + +int json_object_update_existing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +int json_object_update_missing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(!json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +void *json_object_iter(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter(&object->hashtable); +} + +void *json_object_iter_at(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter_at(&object->hashtable, key); +} + +void *json_object_iter_next(json_t *json, void *iter) +{ + json_object_t *object; + + if(!json_is_object(json) || iter == NULL) + return NULL; + + object = json_to_object(json); + return hashtable_iter_next(&object->hashtable, iter); +} + +const char *json_object_iter_key(void *iter) +{ + if(!iter) + return NULL; + + return hashtable_iter_key(iter); +} + +json_t *json_object_iter_value(void *iter) +{ + if(!iter) + return NULL; + + return (json_t *)hashtable_iter_value(iter); +} + +int json_object_iter_set_new(json_t *json, void *iter, json_t *value) +{ + if(!json_is_object(json) || !iter || !value) + return -1; + + hashtable_iter_set(iter, value); + return 0; +} + +void *json_object_key_to_iter(const char *key) +{ + if(!key) + return NULL; + + return hashtable_key_to_iter(key); +} + +static int json_object_equal(json_t *object1, json_t *object2) +{ + const char *key; + json_t *value1, *value2; + + if(json_object_size(object1) != json_object_size(object2)) + return 0; + + json_object_foreach(object1, key, value1) { + value2 = json_object_get(object2, key); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_object_copy(json_t *object) +{ + json_t *result; + + const char *key; + json_t *value; + + result = json_object(); + if(!result) + return NULL; + + json_object_foreach(object, key, value) + json_object_set_nocheck(result, key, value); + + return result; +} + +static json_t *json_object_deep_copy(const json_t *object) +{ + json_t *result; + void *iter; + + result = json_object(); + if(!result) + return NULL; + + /* Cannot use json_object_foreach because object has to be cast + non-const */ + iter = json_object_iter((json_t *)object); + while(iter) { + const char *key; + const json_t *value; + key = json_object_iter_key(iter); + value = json_object_iter_value(iter); + + json_object_set_new_nocheck(result, key, json_deep_copy(value)); + iter = json_object_iter_next((json_t *)object, iter); + } + + return result; +} + + +/*** array ***/ + +json_t *json_array(void) +{ + json_array_t *array = jsonp_malloc(sizeof(json_array_t)); + if(!array) + return NULL; + json_init(&array->json, JSON_ARRAY); + + array->entries = 0; + array->size = 8; + + array->table = jsonp_malloc(array->size * sizeof(json_t *)); + if(!array->table) { + jsonp_free(array); + return NULL; + } + + array->visited = 0; + + return &array->json; +} + +static void json_delete_array(json_array_t *array) +{ + size_t i; + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + jsonp_free(array->table); + jsonp_free(array); +} + +size_t json_array_size(const json_t *json) +{ + if(!json_is_array(json)) + return 0; + + return json_to_array(json)->entries; +} + +json_t *json_array_get(const json_t *json, size_t index) +{ + json_array_t *array; + if(!json_is_array(json)) + return NULL; + array = json_to_array(json); + + if(index >= array->entries) + return NULL; + + return array->table[index]; +} + +int json_array_set_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index >= array->entries) + { + json_decref(value); + return -1; + } + + json_decref(array->table[index]); + array->table[index] = value; + + return 0; +} + +static void array_move(json_array_t *array, size_t dest, + size_t src, size_t count) +{ + memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *)); +} + +static void array_copy(json_t **dest, size_t dpos, + json_t **src, size_t spos, + size_t count) +{ + memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *)); +} + +static json_t **json_array_grow(json_array_t *array, + size_t amount, + int copy) +{ + size_t new_size; + json_t **old_table, **new_table; + + if(array->entries + amount <= array->size) + return array->table; + + old_table = array->table; + + new_size = max(array->size + amount, array->size * 2); + new_table = jsonp_malloc(new_size * sizeof(json_t *)); + if(!new_table) + return NULL; + + array->size = new_size; + array->table = new_table; + + if(copy) { + array_copy(array->table, 0, old_table, 0, array->entries); + jsonp_free(old_table); + return array->table; + } + + return old_table; +} + +int json_array_append_new(json_t *json, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(!json_array_grow(array, 1, 1)) { + json_decref(value); + return -1; + } + + array->table[array->entries] = value; + array->entries++; + + return 0; +} + +int json_array_insert_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + json_t **old_table; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index > array->entries) { + json_decref(value); + return -1; + } + + old_table = json_array_grow(array, 1, 0); + if(!old_table) { + json_decref(value); + return -1; + } + + if(old_table != array->table) { + array_copy(array->table, 0, old_table, 0, index); + array_copy(array->table, index + 1, old_table, index, + array->entries - index); + jsonp_free(old_table); + } + else + array_move(array, index + 1, index, array->entries - index); + + array->table[index] = value; + array->entries++; + + return 0; +} + +int json_array_remove(json_t *json, size_t index) +{ + json_array_t *array; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + if(index >= array->entries) + return -1; + + json_decref(array->table[index]); + + /* If we're removing the last element, nothing has to be moved */ + if(index < array->entries - 1) + array_move(array, index, index + 1, array->entries - index - 1); + + array->entries--; + + return 0; +} + +int json_array_clear(json_t *json) +{ + json_array_t *array; + size_t i; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + array->entries = 0; + return 0; +} + +int json_array_extend(json_t *json, json_t *other_json) +{ + json_array_t *array, *other; + size_t i; + + if(!json_is_array(json) || !json_is_array(other_json)) + return -1; + array = json_to_array(json); + other = json_to_array(other_json); + + if(!json_array_grow(array, other->entries, 1)) + return -1; + + for(i = 0; i < other->entries; i++) + json_incref(other->table[i]); + + array_copy(array->table, array->entries, other->table, 0, other->entries); + + array->entries += other->entries; + return 0; +} + +static int json_array_equal(json_t *array1, json_t *array2) +{ + size_t i, size; + + size = json_array_size(array1); + if(size != json_array_size(array2)) + return 0; + + for(i = 0; i < size; i++) + { + json_t *value1, *value2; + + value1 = json_array_get(array1, i); + value2 = json_array_get(array2, i); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_array_copy(json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append(result, json_array_get(array, i)); + + return result; +} + +static json_t *json_array_deep_copy(const json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append_new(result, json_deep_copy(json_array_get(array, i))); + + return result; +} + +/*** string ***/ + +static json_t *string_create(const char *value, size_t len, int own) +{ + char *v; + json_string_t *string; + + if(!value) + return NULL; + + if(own) + v = (char *)value; + else { + v = jsonp_strndup(value, len); + if(!v) + return NULL; + } + + string = jsonp_malloc(sizeof(json_string_t)); + if(!string) { + if(!own) + jsonp_free(v); + return NULL; + } + json_init(&string->json, JSON_STRING); + string->value = v; + string->length = len; + + return &string->json; +} + +json_t *json_string_nocheck(const char *value) +{ + if(!value) + return NULL; + + return string_create(value, strlen(value), 0); +} + +json_t *json_stringn_nocheck(const char *value, size_t len) +{ + return string_create(value, len, 0); +} + +/* this is private; "steal" is not a public API concept */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len) +{ + return string_create(value, len, 1); +} + +json_t *json_string(const char *value) +{ + if(!value) + return NULL; + + return json_stringn(value, strlen(value)); +} + +json_t *json_stringn(const char *value, size_t len) +{ + if(!value || !utf8_check_string(value, len)) + return NULL; + + return json_stringn_nocheck(value, len); +} + +const char *json_string_value(const json_t *json) +{ + if(!json_is_string(json)) + return NULL; + + return json_to_string(json)->value; +} + +size_t json_string_length(const json_t *json) +{ + if(!json_is_string(json)) + return 0; + + return json_to_string(json)->length; +} + +int json_string_set_nocheck(json_t *json, const char *value) +{ + if(!value) + return -1; + + return json_string_setn_nocheck(json, value, strlen(value)); +} + +int json_string_setn_nocheck(json_t *json, const char *value, size_t len) +{ + char *dup; + json_string_t *string; + + if(!json_is_string(json) || !value) + return -1; + + dup = jsonp_strndup(value, len); + if(!dup) + return -1; + + string = json_to_string(json); + jsonp_free(string->value); + string->value = dup; + string->length = len; + + return 0; +} + +int json_string_set(json_t *json, const char *value) +{ + if(!value) + return -1; + + return json_string_setn(json, value, strlen(value)); +} + +int json_string_setn(json_t *json, const char *value, size_t len) +{ + if(!value || !utf8_check_string(value, len)) + return -1; + + return json_string_setn_nocheck(json, value, len); +} + +static void json_delete_string(json_string_t *string) +{ + jsonp_free(string->value); + jsonp_free(string); +} + +static int json_string_equal(json_t *string1, json_t *string2) +{ + json_string_t *s1, *s2; + + if(!json_is_string(string1) || !json_is_string(string2)) + return 0; + + s1 = json_to_string(string1); + s2 = json_to_string(string2); + return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length); +} + +static json_t *json_string_copy(const json_t *string) +{ + json_string_t *s; + + if(!json_is_string(string)) + return NULL; + + s = json_to_string(string); + return json_stringn_nocheck(s->value, s->length); +} + + +/*** integer ***/ + +json_t *json_integer(json_int_t value) +{ + json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t)); + if(!integer) + return NULL; + json_init(&integer->json, JSON_INTEGER); + + integer->value = value; + return &integer->json; +} + +json_int_t json_integer_value(const json_t *json) +{ + if(!json_is_integer(json)) + return 0; + + return json_to_integer(json)->value; +} + +int json_integer_set(json_t *json, json_int_t value) +{ + if(!json_is_integer(json)) + return -1; + + json_to_integer(json)->value = value; + + return 0; +} + +static void json_delete_integer(json_integer_t *integer) +{ + jsonp_free(integer); +} + +static int json_integer_equal(json_t *integer1, json_t *integer2) +{ + return json_integer_value(integer1) == json_integer_value(integer2); +} + +static json_t *json_integer_copy(const json_t *integer) +{ + return json_integer(json_integer_value(integer)); +} + + +/*** real ***/ + +json_t *json_real(double value) +{ + json_real_t *real; + + if(isnan(value) || isinf(value)) + return NULL; + + real = jsonp_malloc(sizeof(json_real_t)); + if(!real) + return NULL; + json_init(&real->json, JSON_REAL); + + real->value = value; + return &real->json; +} + +double json_real_value(const json_t *json) +{ + if(!json_is_real(json)) + return 0; + + return json_to_real(json)->value; +} + +int json_real_set(json_t *json, double value) +{ + if(!json_is_real(json) || isnan(value) || isinf(value)) + return -1; + + json_to_real(json)->value = value; + + return 0; +} + +static void json_delete_real(json_real_t *real) +{ + jsonp_free(real); +} + +static int json_real_equal(json_t *real1, json_t *real2) +{ + return json_real_value(real1) == json_real_value(real2); +} + +static json_t *json_real_copy(const json_t *real) +{ + return json_real(json_real_value(real)); +} + + +/*** number ***/ + +double json_number_value(const json_t *json) +{ + if(json_is_integer(json)) + return (double)json_integer_value(json); + else if(json_is_real(json)) + return json_real_value(json); + else + return 0.0; +} + + +/*** simple values ***/ + +json_t *json_true(void) +{ + static json_t the_true = {JSON_TRUE, (size_t)-1}; + return &the_true; +} + + +json_t *json_false(void) +{ + static json_t the_false = {JSON_FALSE, (size_t)-1}; + return &the_false; +} + + +json_t *json_null(void) +{ + static json_t the_null = {JSON_NULL, (size_t)-1}; + return &the_null; +} + + +/*** deletion ***/ + +void json_delete(json_t *json) +{ + if(json_is_object(json)) + json_delete_object(json_to_object(json)); + + 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)); + + /* json_delete is not called for true, false or null */ +} + + +/*** equality ***/ + +int json_equal(json_t *json1, json_t *json2) +{ + if(!json1 || !json2) + return 0; + + if(json_typeof(json1) != json_typeof(json2)) + return 0; + + /* this covers true, false and null as they are singletons */ + 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; +} + + +/*** copying ***/ + +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; + + return NULL; +} + +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; + + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/json.cpp Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,341 @@ +/* + * json.cpp -- C++14 JSON manipulation using jansson + * + * Copyright (c) 2015-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <jansson.h> + +#include <sstream> + +#include "json.hpp" + +namespace json { + +namespace { + +void readObject(Value &parent, json_t *object); +void readArray(Value &parent, json_t *array); + +Value readValue(json_t *v) +{ + if (json_is_null(v)) + return Value(nullptr); + if (json_is_string(v)) + return Value(json_string_value(v)); + if (json_is_real(v)) + return Value(json_number_value(v)); + if (json_is_integer(v)) + return Value(static_cast<int>(json_integer_value(v))); + if (json_is_boolean(v)) + return Value(json_boolean_value(v)); + if (json_is_object(v)) { + Value object(Type::Object); + + readObject(object, v); + + return object; + } + if (json_is_array(v)) { + Value array(Type::Array); + + readArray(array, v); + + return array; + } + + return Value(); +} + +void readObject(Value &parent, json_t *object) +{ + const char *key; + json_t *value; + + json_object_foreach(object, key, value) + parent.insert(key, readValue(value)); +} + +void readArray(Value &parent, json_t *array) +{ + size_t index; + json_t *value; + + json_array_foreach(array, index, value) + parent.append(readValue(value)); +} + +template <typename Func, typename... Args> +Value convert(Func fn, Args&&... args) +{ + json_error_t error; + json_t *json = fn(std::forward<Args>(args)..., &error); + + if (json == nullptr) + throw Error(error.text, error.source, error.line, error.column, error.position); + + Value value; + + if (json_is_object(json)) { + value = Value(Type::Object); + readObject(value, json); + } else { + value = Value(Type::Array); + readArray(value, json); + } + + json_decref(json); + + return value; +} + +std::string indent(int param, int level) +{ + std::string str; + + if (param < 0) + str = std::string(level, '\t'); + else if (param > 0) + str = std::string(param * level, ' '); + + return str; +} + +} // !namespace + +void Value::copy(const Value &other) +{ + switch (other.m_type) { + case Type::Array: + new (&m_array) std::vector<Value>(other.m_array); + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Int: + m_integer = other.m_integer; + break; + case Type::Object: + new (&m_object) std::map<std::string, Value>(other.m_object); + break; + case Type::Real: + m_number = other.m_number; + break; + case Type::String: + new (&m_string) std::string(other.m_string); + break; + default: + break; + } + + m_type = other.m_type; +} + +void Value::move(Value &&other) +{ + switch (other.m_type) { + case Type::Array: + new (&m_array) std::vector<Value>(std::move(other.m_array)); + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Int: + m_integer = other.m_integer; + break; + case Type::Object: + new (&m_object) std::map<std::string, Value>(std::move(other.m_object)); + break; + case Type::Real: + m_number = other.m_number; + break; + case Type::String: + new (&m_string) std::string(std::move(other.m_string)); + break; + default: + break; + } + + m_type = other.m_type; +} + +Value::Value(Type type) + : m_type(type) +{ + switch (m_type) { + case Type::Array: + new (&m_array) std::vector<Value>(); + break; + case Type::Boolean: + m_boolean = false; + break; + case Type::Int: + m_integer = 0; + break; + case Type::Object: + new (&m_object) std::map<std::string, Value>(); + break; + case Type::Real: + m_number = 0; + break; + case Type::String: + new (&m_string) std::string(); + break; + default: + break; + } +} + +Value::~Value() +{ + switch (m_type) { + case Type::Array: + m_array.~vector<Value>(); + break; + case Type::Object: + m_object.~map<std::string, Value>(); + break; + case Type::String: + m_string.~basic_string(); + break; + default: + break; + } +} + +std::string Value::toString(bool coerce) const +{ + std::string result; + + if (m_type == Type::String) + result = m_string; + else if (coerce) + result = toJson(); + + return result; +} + +std::string Value::toJson(int level, int current) const +{ + std::ostringstream oss; + + switch (m_type) { + case Type::Array: { + oss << '[' << (level != 0 ? "\n" : ""); + + unsigned total = m_array.size(); + unsigned i = 0; + for (const auto &v : m_array) { + oss << indent(level, current + 1) << v.toJson(level, current + 1); + oss << (++i < total ? "," : ""); + oss << (level != 0 ? "\n" : ""); + } + + oss << (level != 0 ? indent(level, current) : "") << ']'; + break; + } + case Type::Boolean: + oss << (m_boolean ? "true" : "false"); + break; + case Type::Int: + oss << m_integer; + break; + case Type::Null: + oss << "null"; + break; + case Type::Object: { + oss << '{' << (level != 0 ? "\n" : ""); + + unsigned total = m_object.size(); + unsigned i = 0; + for (const auto &pair : m_object) { + oss << indent(level, current + 1); + + // Key and :. + oss << "\"" << pair.first << "\":" << (level != 0 ? " " : ""); + + // Value. + oss << pair.second.toJson(level, current + 1); + + // Comma, new line if needed. + oss << (++i < total ? "," : "") << (level != 0 ? "\n" : ""); + } + + oss << (level != 0 ? indent(level, current) : "") << '}'; + break; + } + case Type::Real: + oss << m_number; + break; + case Type::String: + oss << "\"" << escape(m_string) << "\""; + break; + default: + break; + } + + return oss.str(); +} + +std::string escape(const std::string &value) +{ + std::string result; + + for (auto it = value.begin(); it != value.end(); ++it) { + switch (*it) { + case '\\': + result += "\\\\"; + break; + case '/': + result += "\\/"; + break; + case '"': + result += "\\\""; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + result += *it; + break; + } + } + + return result; +} + +Value fromString(const std::string &buffer) +{ + return convert(json_loads, buffer.c_str(), 0); +} + +Value fromFile(const std::string &path) +{ + return convert(json_load_file, path.c_str(), 0); +} + +} // !json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/json.hpp Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,1233 @@ +/* + * json.hpp -- C++14 JSON manipulation using jansson + * + * Copyright (c) 2015-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef JSON_HPP +#define JSON_HPP + +/** + * \file json.hpp + * \brief C++14 JSON manipulation using jansson. + * \author David Demelier <markand@malikania.fr> + */ + +/** + * \page Json Json + * \brief C++14 JSON manipulation using jansson. + * + * This library uses Jansson for parsing files only. It then converts the structure into internal storage. + * + * ## Creating objects and arrays + * + * The following code shows how you can easily create objects or arrays. + * + * ````cpp + * auto object = json::object({ + * { "x", 1 }, + * { "y", 2 } + * }); + * + * auto array = json::array({ 123, 456, "hello" }); + * ```` + * + * ## Reading sources + * + * You can use json::fromFile and json::fromString to load JSON documents. + * + * ````cpp + * #include <iostream> + * + * #include "json.h" + * + * try { + * auto value = json::fromFile("foo.json"); + * } catch (const std::exception &ex) { + * std::cerr << ex.what() << std::endl; + * } + * ```` + */ + +#include <cassert> +#include <exception> +#include <initializer_list> +#include <map> +#include <string> +#include <utility> +#include <vector> + +/** + * Json namespace. + */ +namespace json { + +/** + * \enum Type + * \brief Type of Value. + */ +enum class Type { + Array, //!< Value is an array [] + Boolean, //!< Value is boolean + Int, //!< Value is integer + Null, //!< Value is defined to null + Object, //!< Value is object {} + Real, //!< Value is float + String //!< Value is unicode string +}; + +/** + * \class Error + * \brief Error description. + */ +class Error : public std::exception { +private: + std::string m_text; + std::string m_source; + int m_line; + int m_column; + int m_position; + +public: + /** + * Create the error. + * + * \param text the text message + * \param source the source (e.g. file name) + * \param line the line number + * \param column the column number + * \param position the position + */ + inline Error(std::string text, std::string source, int line, int column, int position) noexcept + : m_text(std::move(text)) + , m_source(std::move(source)) + , m_line(line) + , m_column(column) + , m_position(position) + { + } + + /** + * Get the error message. + * + * \return the text + */ + inline const std::string &text() const noexcept + { + return m_text; + } + + /** + * Get the source (e.g. a file name). + * + * \return the source + */ + inline const std::string &source() const noexcept + { + return m_source; + } + + /** + * Get the line. + * + * \return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column. + * + * \return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Get the position. + * + * \return the position + */ + inline int position() const noexcept + { + return m_position; + } + + /** + * Get the error message. + * + * \return the message + */ + const char *what() const noexcept override + { + return m_text.c_str(); + } +}; + +/** + * \class Iterator + * \brief This is the base class for iterator and const_iterator + * + * This iterator works for both arrays and objects. Because of that purpose, it is only available + * as forward iterator. + * + * When iterator comes from an object, you can use key() otherwise you can use index(). + */ +template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType> +class Iterator : public std::iterator<std::forward_iterator_tag, ValueType> { +private: + friend class Value; + + ValueType *m_parent{nullptr}; + ArrayIteratorType m_ita; + ObjectIteratorType m_itm; + + inline void increment() + { + if (m_parent->isObject()) + m_itm++; + else + m_ita++; + } + + inline Iterator(ValueType *parent, ObjectIteratorType it) + : m_parent(parent) + , m_itm(it) + { + assert(parent); + } + + inline Iterator(ValueType *parent, ArrayIteratorType it) + : m_parent(parent) + , m_ita(it) + { + assert(parent); + } + +public: + /** + * Default constructor. + */ + Iterator() = default; + + /** + * Get the iterator key (for objects). + * + * \pre iterator must be dereferenceable + * \pre iterator must come from object + * \return the key + */ + inline const std::string &key() const noexcept + { + assert(m_parent && m_parent->isObject()); + assert(m_itm != m_parent->m_object.end()); + + return m_itm->first; + } + + /** + * Get the iterator position (for arrays). + * + * \pre iterator must be dereferenceable + * \pre iterator must come from arrays + * \return the index + */ + inline unsigned index() const noexcept + { + assert(m_parent && m_parent->isArray()); + assert(m_ita != m_parent->m_array.end()); + + return std::distance(m_parent->m_array.begin(), m_ita); + } + + /** + * Dereference the iterator. + * + * \pre iterator be dereferenceable + * \return the value + */ + inline ValueType &operator*() noexcept + { + assert(m_parent); + assert((m_parent->isArray() && m_ita != m_parent->m_array.end()) || + (m_parent->isObject() && m_itm != m_parent->m_object.end())); + + return (m_parent->m_type == Type::Object) ? m_itm->second : *m_ita; + } + + /** + * Dereference the iterator as a pointer. + * + * \pre iterator must be dereferenceable + * \return the value + */ + inline ValueType *operator->() noexcept + { + assert(m_parent); + assert((m_parent->isArray() && m_ita != m_parent->m_array.end()) || + (m_parent->isObject() && m_itm != m_parent->m_object.end())); + + return (m_parent->m_type == Type::Object) ? &m_itm->second : &(*m_ita); + } + + /** + * Increment the iterator. (Prefix version). + * + * \pre iterator must be dereferenceable + * \return *this; + */ + inline Iterator &operator++() noexcept + { + assert(m_parent); + assert((m_parent->isArray() && m_ita != m_parent->m_array.end()) || + (m_parent->isObject() && m_itm != m_parent->m_object.end())); + + increment(); + + return *this; + } + + /** + * Increment the iterator. (Postfix version). + * + * \pre iterator must be dereferenceable + * \return *this; + */ + inline Iterator &operator++(int) noexcept + { + assert(m_parent); + assert((m_parent->isArray() && m_ita != m_parent->m_array.end()) || + (m_parent->isObject() && m_itm != m_parent->m_object.end())); + + increment(); + + return *this; + } + + /** + * Compare two iterators. + * + * \param it the first iterator + * \return true if they are same + */ + bool operator==(const Iterator &it) const noexcept + { + return m_parent == it.m_parent && m_itm == it.m_itm && m_ita == it.m_ita; + } + + /** + * Test if the iterator is different. + * + * \param it the iterator + * \return true if they are different + */ + inline bool operator!=(const Iterator &it) const noexcept + { + return !(*this == it); + } +}; + +/** + * \class Value + * \brief Generic JSON value wrapper. + */ +class Value { +private: + Type m_type{Type::Null}; + + union { + double m_number; + bool m_boolean; + int m_integer; + std::string m_string; + std::vector<Value> m_array; + std::map<std::string, Value> m_object; + }; + + void copy(const Value &); + void move(Value &&); + std::string toJson(int indent, int current) const; + + friend class Iterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>; + friend class Iterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>; + +public: + /** + * Forward iterator. + */ + using iterator = Iterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>; + + /** + * Const forward iterator. + */ + using const_iterator = Iterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>; + + /** + * Construct a null value. + */ + inline Value() noexcept + { + } + + /** + * Create a value with a specified type, this is usually only needed when you want to create an object or + * an array. + * + * For any other types, initialize with sane default value. + * + * \param type the type + */ + Value(Type type); + + /** + * Construct a null value. + */ + inline Value(std::nullptr_t) noexcept + : m_type(Type::Null) + { + } + + /** + * Construct a boolean value. + * + * \param value the boolean value + */ + inline Value(bool value) noexcept + : m_type(Type::Boolean) + , m_boolean(value) + { + } + + /** + * Create value from integer. + * + * \param value the value + */ + inline Value(int value) noexcept + : m_type(Type::Int) + , m_integer(value) + { + } + + /** + * Construct a value from a C-string. + * + * \param value the C-string + */ + inline Value(const char *value) + : m_type(Type::String) + { + new (&m_string) std::string{value ? value : ""}; + } + + /** + * Construct a number value. + * + * \param value the real value + */ + inline Value(double value) noexcept + : m_type(Type::Real) + , m_number(value) + { + } + + /** + * Construct a string value. + * + * \param value the string + */ + inline Value(std::string value) noexcept + : m_type(Type::String) + { + new (&m_string) std::string(std::move(value)); + } + + /** + * Create an object from a map. + * + * \param values the values + * \see fromObject + */ + inline Value(std::map<std::string, Value> values) + : Value(Type::Object) + { + for (const auto &pair : values) + insert(pair.first, pair.second); + } + + /** + * Create an array from a vector. + * + * \param values the values + * \see fromArray + */ + inline Value(std::vector<Value> values) + : Value(Type::Array) + { + for (Value value : values) + append(std::move(value)); + } + + /** + * Move constructor. + * + * \param other the value to move from + */ + inline Value(Value &&other) + { + move(std::move(other)); + } + + /** + * Copy constructor. + * + * \param other the value to copy from + */ + inline Value(const Value &other) + { + copy(other); + } + + /** + * Copy operator. + * + * \param other the value to copy from + * \return *this + */ + inline Value &operator=(const Value &other) + { + copy(other); + + return *this; + } + + /** + * Move operator. + * + * \param other the value to move from + * \return this + */ + inline Value &operator=(Value &&other) + { + move(std::move(other)); + + return *this; + } + + /** + * Destructor. + */ + ~Value(); + + /** + * Get an iterator to the beginning. + * + * \pre must be an array or object + * \return the iterator + */ + inline iterator begin() noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? iterator(this, m_object.begin()) : iterator(this, m_array.begin()); + } + + /** + * Overloaded function. + * + * \pre must be an array or object + * \return the iterator + */ + inline const_iterator begin() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(this, m_object.begin()) : const_iterator(this, m_array.begin()); + } + + /** + * Overloaded function. + * + * \pre must be an array or object + * \return the iterator + */ + inline const_iterator cbegin() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(this, m_object.cbegin()) : const_iterator(this, m_array.cbegin()); + } + + /** + * Get an iterator to the end. + * + * \pre must be an array or object + * \return the iterator + */ + inline iterator end() noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? iterator(this, m_object.end()) : iterator(this, m_array.end()); + } + + /** + * Get an iterator to the end. + * + * \pre must be an array or object + * \return the iterator + */ + inline const_iterator end() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(this, m_object.end()) : const_iterator(this, m_array.end()); + } + + /** + * Get an iterator to the end. + * + * \pre must be an array or object + * \return the iterator + */ + inline const_iterator cend() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(this, m_object.cend()) : const_iterator(this, m_array.cend()); + } + + /** + * Get the value type. + * + * \return the type + */ + inline Type typeOf() const noexcept + { + return m_type; + } + + /** + * Get the value as boolean. + * + * \return the value or false if not a boolean + */ + inline bool toBool() const noexcept + { + return m_type != Type::Boolean ? false : m_boolean; + } + + /** + * Get the value as integer. + * + * \return the value or 0 if not a integer + */ + inline int toInt() const noexcept + { + return m_type != Type::Int ? 0 : m_integer; + } + + /** + * Get the value as real. + * + * \return the value or 0 if not a real + */ + inline double toReal() const noexcept + { + return m_type != Type::Real ? 0 : m_number; + } + + /** + * Get the value as string. + * + * \param coerce set to true to coerce the value if not a string + * \return the value or empty string if not a string + */ + std::string toString(bool coerce = false) const; + + /** + * Check if the value is boolean type. + * + * \return true if boolean + */ + inline bool isBool() const noexcept + { + return m_type == Type::Boolean; + } + + /** + * Check if the value is integer type. + * + * \return true if integer + */ + inline bool isInt() const noexcept + { + return m_type == Type::Int; + } + + /** + * Check if the value is object type. + * + * \return true if object + */ + inline bool isObject() const noexcept + { + return m_type == Type::Object; + } + + /** + * Check if the value is array type. + * + * \return true if array + */ + inline bool isArray() const noexcept + { + return m_type == Type::Array; + } + + /** + * Check if the value is integer or real type. + * + * \return true if integer or real + * \see toInt + * \see toReal + */ + inline bool isNumber() const noexcept + { + return m_type == Type::Real || m_type == Type::Int; + } + + /** + * Check if the value is real type. + * + * \return true if real + */ + inline bool isReal() const noexcept + { + return m_type == Type::Real; + } + + /** + * Check if the value is null type. + * + * \return true if null + */ + inline bool isNull() const noexcept + { + return m_type == Type::Null; + } + + /** + * Check if the value is string type. + * + * \return true if string + */ + inline bool isString() const noexcept + { + return m_type == Type::String; + } + + /** + * Get the array or object size. + * + * \pre must be an array or object + * \return the size + */ + inline unsigned size() const noexcept + { + assert(isArray() || isObject()); + + if (m_type == Type::Object) + return m_object.size(); + + return m_array.size(); + } + + /** + * Remove all the values. + * + * \pre must be an array or an object + */ + inline void clear() noexcept + { + assert(isArray() || isObject()); + + if (m_type == Type::Array) + m_array.clear(); + else + m_object.clear(); + } + + /* + * Array functions. + * ---------------------------------------------------------- + */ + + /** + * Get the value at the specified position or the defaultValue if position is out of bounds. + * + * \param position the position + * \param defaultValue the value replacement + * \return the value or defaultValue + */ + template <typename DefaultValue> + inline Value valueOr(unsigned position, DefaultValue &&defaultValue) const + { + if (m_type != Type::Array || position >= m_array.size()) + return defaultValue; + + return m_array[position]; + } + + /** + * Overloaded function with type check. + * + * \param position the position + * \param type the requested type + * \param defaultValue the value replacement + * \return the value or defaultValue + */ + template <typename DefaultValue> + inline Value valueOr(unsigned position, Type type, DefaultValue &&defaultValue) const + { + if (m_type != Type::Array || position >= m_array.size() || m_array[position].typeOf() != type) + return defaultValue; + + return m_array[position]; + } + + /** + * Get a value at the specified index. + * + * \pre must be an array + * \param position the position + * \return the value + * \throw std::out_of_range if out of bounds + */ + inline const Value &at(unsigned position) const + { + assert(isArray()); + + return m_array.at(position); + } + + /** + * Overloaded function. + * + * \pre must be an array + * \param position the position + * \return the value + * \throw std::out_of_range if out of bounds + */ + inline Value &at(unsigned position) + { + assert(isArray()); + + return m_array.at(position); + } + + /** + * Get a value at the specified index. + * + * \pre must be an array + * \pre position must be valid + * \param position the position + * \return the value + */ + inline const Value &operator[](unsigned position) const + { + assert(isArray()); + assert(position < m_array.size()); + + return m_array[position]; + } + + /** + * Overloaded function. + * + * \pre must be an array + * \pre position must be valid + * \param position the position + * \return the value + */ + inline Value &operator[](unsigned position) + { + assert(isArray()); + assert(position < m_array.size()); + + return m_array[position]; + } + + /** + * Push a value to the beginning of the array. + * + * \pre must be an array + * \param value the value to push + */ + inline void push(const Value &value) + { + assert(isArray()); + + m_array.insert(m_array.begin(), value); + } + + /** + * Overloaded function. + * + * \pre must be an array + * \param value the value to push + */ + inline void push(Value &&value) + { + assert(isArray()); + + m_array.insert(m_array.begin(), std::move(value)); + } + + /** + * Insert a value at the specified position. + * + * \pre must be an array + * \pre position must be valid + * \param position the position + * \param value the value to push + */ + inline void insert(unsigned position, const Value &value) + { + assert(isArray()); + assert(position <= m_array.size()); + + m_array.insert(m_array.begin() + position, value); + } + + /** + * Overloaded function. + * + * \pre must be an array + * \pre position must be valid + * \param position the position + * \param value the value to push + */ + inline void insert(unsigned position, Value &&value) + { + assert(isArray()); + assert(position <= m_array.size()); + + m_array.insert(m_array.begin() + position, std::move(value)); + } + + /** + * Add a new value to the end. + * + * \pre must be an array + * \param value the value to append + */ + inline void append(const Value &value) + { + assert(isArray()); + + m_array.push_back(value); + } + + /** + * Overloaded function. + * + * \pre must be an array + * \param value the value to append + */ + inline void append(Value &&value) + { + assert(isArray()); + + m_array.push_back(std::move(value)); + } + + /** + * Remove a value at the specified position. + * + * \pre must be an array + * \pre position must be valid + * \param position the position + */ + inline void erase(unsigned position) + { + assert(isArray()); + assert(position < m_array.size()); + + m_array.erase(m_array.begin() + position); + } + + /* + * Object functions. + * ---------------------------------------------------------- + */ + + /** + * Get the value at the specified key or the defaultValue if key is absent. + * + * \param name the name + * \param defaultValue the value replacement + * \return the value or defaultValue + */ + template <typename DefaultValue> + Value valueOr(const std::string &name, DefaultValue &&defaultValue) const + { + if (m_type != Type::Object) + return defaultValue; + + auto it = m_object.find(name); + + if (it == m_object.end()) + return defaultValue; + + return it->second; + } + + /** + * Overloaded function with type check. + * + * \param name the name + * \param type the requested type + * \param defaultValue the value replacement + * \return the value or defaultValue + */ + template <typename DefaultValue> + Value valueOr(const std::string &name, Type type, DefaultValue &&defaultValue) const + { + if (m_type != Type::Object) + return defaultValue; + + auto it = m_object.find(name); + + if (it == m_object.end() || it->second.typeOf() != type) + return defaultValue; + + return it->second; + } + + /** + * Get a value from the object. + * + * \pre must be an object + * \param name the value key + * \return the value + * \throw std::out_of_range if not found + */ + inline const Value &at(const std::string &name) const + { + assert(isObject()); + + return m_object.at(name); + } + + /** + * Overloaded function. + * + * \pre must be an object + * \param name the value key + * \return the value + * \throw std::out_of_range if not found + */ + inline Value &at(const std::string &name) + { + assert(isObject()); + + return m_object.at(name); + } + + /** + * Get a value from the object. + * + * \pre must be an object + * \param name the value key + * \return the value + */ + inline Value &operator[](const std::string &name) + { + assert(isObject()); + + return m_object[name]; + } + + /** + * Find a value by key. + * + * \pre must be an object + * \param key the property key + * \return the iterator or past the end if not found + */ + inline iterator find(const std::string &key) + { + assert(isObject()); + + return iterator(this, m_object.find(key)); + } + + /** + * Overloaded function. + * + * \pre must be an object + * \param key the property key + * \return the iterator or past the end if not found + */ + inline const_iterator find(const std::string &key) const + { + assert(isObject()); + + return const_iterator(this, m_object.find(key)); + } + + /** + * Insert a new value. + * + * \pre must be an object + * \param name the key + * \param value the value + */ + inline void insert(std::string name, const Value &value) + { + assert(isObject()); + + m_object.insert({std::move(name), value}); + } + + /** + * Overloaded function. + * + * \pre must be an object + * \param name the key + * \param value the value + */ + inline void insert(std::string name, Value &&value) + { + assert(isObject()); + + m_object.insert({std::move(name), std::move(value)}); + } + + /** + * Check if a value exists. + * + * \pre must be an object + * \param key the key value + * \return true if exists + */ + inline bool contains(const std::string &key) const noexcept + { + assert(isObject()); + + return m_object.find(key) != m_object.end(); + } + + /** + * Remove a value of the specified key. + * + * \pre must be an object + * \param key the value key + */ + inline void erase(const std::string &key) + { + assert(isObject()); + + m_object.erase(key); + } + + /** + * Return this value as JSon representation. + * + * \param indent the indentation to use (0 == compact, < 0 == tabs, > 0 == number of spaces) + * \return the string + */ + inline std::string toJson(int indent = 2) const + { + return toJson(indent, 0); + } +}; + +/** + * Escape the input. + * + * \param input the input + * \return the escaped string + */ +std::string escape(const std::string &input); + +/** + * Convenient function to create an empty array. + * + * \return an empty array + */ +inline Value array() +{ + return Value(Type::Array); +} + +/** + * Convenient function for creating array from initializer list. + * + * \param values the values + * \return the array + */ +inline Value array(std::initializer_list<Value> values) +{ + return Value(std::vector<Value>(values.begin(), values.end())); +} + +/** + * Convenient function to create an empty object. + * + * \return an empty object + */ +inline Value object() +{ + return Value(Type::Object); +} + +/** + * Convenient function for creating object from initializer list. + * + * \param values the values + * \return the object + */ +inline Value object(std::initializer_list<std::pair<std::string, Value>> values) +{ + return Value(std::map<std::string, Value>(values.begin(), values.end())); +} + +/** + * Construct a value from a buffer. + * + * \param data the JSON data + * \return the parsed value + * \throw Error on errors + */ +Value fromString(const std::string &data); + +/** + * Construct a value from a file. + * + * \param path the path to the file + * \return the parsed value + * \throw Error on errors + */ +Value fromFile(const std::string &path); + +} // !json + +#endif // !JSON_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/test/data/array-all.json Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,9 @@ +[ + 123, + 9.2, + false, + true, + null, + {}, + [] +]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/test/data/array.json Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,1 @@ +[1, 2, 3]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/test/data/object-all.json Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,9 @@ +{ + "integer": 123, + "real": 9.2, + "false": false, + "true": true, + "null": null, + "object": {}, + "array": [] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/test/data/object.json Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,4 @@ +{ + "name": "simple", + "description": "basic JSON file" +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/test/data/simple.json Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,2 @@ +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/json/test/main.cpp Wed Jun 01 17:09:12 2016 +0200 @@ -0,0 +1,963 @@ +/* + * main.cpp -- test the jansson wrapper + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <set> +#include <unordered_map> + +#include <gtest/gtest.h> + +#include "json.hpp" + +/* + * Miscellaneous. + * ------------------------------------------------------------------ + */ + +TEST(Misc, copy) +{ + json::Value object(json::Type::Object); + + object.insert("integer", 123); + object.insert("true", true); + + json::Value object2(object); + + ASSERT_TRUE(object2.isObject()); + ASSERT_EQ(123, object2["integer"].toInt()); + ASSERT_TRUE(object2["true"].toBool()); +} + +TEST(Misc, copyAssign) +{ + json::Value object(json::Type::Object); + + { + json::Value tmp(json::Type::Object); + + tmp.insert("integer", 123); + tmp.insert("true", true); + + object = tmp; + } + + ASSERT_TRUE(object.isObject()); + ASSERT_EQ(123, object["integer"].toInt()); + ASSERT_TRUE(object["true"].toBool()); +} + +TEST(Misc, move) +{ + json::Value object(123); + json::Value object2(std::move(object)); + + ASSERT_TRUE(object2.isInt()); + ASSERT_EQ(123, object2.toInt()); +} + +TEST(Misc, moveAssign) +{ + json::Value object(123); + json::Value object2; + + object2 = std::move(object); + + ASSERT_TRUE(object2.isInt()); + ASSERT_EQ(123, object2.toInt()); +} + +TEST(Misc, escape) +{ + std::string input("\\/\"\b\f\n\r\t"); + std::string expected("\\\\\\/\\\"\\b\\f\\n\\r\\t"); + + ASSERT_EQ(expected, json::escape(input)); +} + +/* + * json::Value constructors. + * ------------------------------------------------------------------ + */ + +TEST(Constructors, boolean) +{ + try { + json::Value value(true); + + ASSERT_TRUE(value.isBool()); + ASSERT_TRUE(value.toBool()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, integer) +{ + try { + json::Value value(123); + + ASSERT_TRUE(value.isInt()); + ASSERT_TRUE(value.isNumber()); + ASSERT_FALSE(value.isReal()); + ASSERT_EQ(123, value.toInt()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, null) +{ + try { + json::Value value(nullptr); + + ASSERT_TRUE(value.isNull()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, real) +{ + try { + json::Value value(9.2); + + ASSERT_TRUE(value.isNumber()); + ASSERT_TRUE(value.isNumber()); + ASSERT_FALSE(value.isInt()); + ASSERT_EQ(9.2, value.toReal()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, string) +{ + try { + json::Value value("hello"); + + ASSERT_TRUE(value.isString()); + ASSERT_EQ("hello", value.toString()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Constructors, emptyObject) +{ + json::Value object(json::Type::Object); + + ASSERT_TRUE(object.isObject()); + ASSERT_EQ(0U, object.size()); +} + +TEST(Constructors, emptyObject2) +{ + json::Value object = json::object(); + + ASSERT_TRUE(object.isObject()); + ASSERT_EQ(0U, object.size()); +} + +TEST(Constructors, emptyArray) +{ + json::Value array(json::Type::Array); + + ASSERT_TRUE(array.isArray()); + ASSERT_EQ(0U, array.size()); +} + +TEST(Constructors, emptyArray2) +{ + json::Value array = json::array(); + + ASSERT_TRUE(array.isArray()); + ASSERT_EQ(0U, array.size()); +} + +/* + * Object. + * ------------------------------------------------------------------ + */ + +TEST(Object, set) +{ + try { + json::Value object(json::Type::Object); + + object.insert("integer", 123); + object.insert("string", "hello"); + object.insert("true", true); + + ASSERT_EQ(123, object["integer"].toInt()); + ASSERT_EQ("hello", object["string"].toString()); + ASSERT_TRUE(object["true"].toBool()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Object, clear) +{ + try { + json::Value object(json::Type::Object); + + object.insert("integer", 123); + object.insert("string", "hello"); + object.insert("true", true); + + object.clear(); + + ASSERT_EQ(0, static_cast<int>(object.size())); + ASSERT_FALSE(object.contains("integer")); + ASSERT_FALSE(object.contains("string")); + ASSERT_FALSE(object.contains("true")); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Object, erase) +{ + try { + json::Value object(json::Type::Object); + + object.insert("integer", 123); + object.insert("string", "hello"); + object.insert("true", true); + + object.erase("integer"); + + ASSERT_EQ(2, static_cast<int>(object.size())); + ASSERT_FALSE(object.contains("integer")); + ASSERT_TRUE(object.contains("string")); + ASSERT_TRUE(object.contains("true")); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Object, valueOr) +{ + json::Value object = json::object({ + { "x", 10 }, + { "y", 20 } + }); + + ASSERT_EQ(10, object.valueOr("x", -9999).toInt()); + ASSERT_EQ(20, object.valueOr("y", -9999).toInt()); + ASSERT_EQ(-9999, object.valueOr("not-found", -9999).toInt()); +} + +TEST(Object, valueOr2) +{ + json::Value object = json::object({ + { "x", 10 }, + { "y", 20 } + }); + + ASSERT_EQ(10, object.valueOr("x", json::Type::Int, -9999).toInt()); + ASSERT_EQ(20, object.valueOr("y", json::Type::Int, -9999).toInt()); + ASSERT_EQ(-9999, object.valueOr("x", json::Type::String, -9999).toInt()); + ASSERT_EQ(-9999, object.valueOr("y", json::Type::Boolean, -9999).toInt()); + ASSERT_EQ(-9999, object.valueOr("not-found", -9999).toInt()); +} + +TEST(ObjectInitializer, simple) +{ + try { + json::Value object = json::object({ + { "username", "jean" }, + { "age", 99 } + }); + + ASSERT_EQ(2, static_cast<int>(object.size())); + ASSERT_EQ("jean", object["username"].toString()); + ASSERT_EQ(99, object["age"].toInt()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST(ObjectInitializer, deep) +{ + try { + json::Value object = json::object({ + { "username", "jean" }, + { "age", 99 }, + { "network", json::object({ + { "port", 9999 }, + { "host", "localhost" } + }) + } + }); + + // First. + ASSERT_EQ(3, static_cast<int>(object.size())); + ASSERT_EQ("jean", object["username"].toString()); + ASSERT_EQ(99, object["age"].toInt()); + + // Second. + json::Value network = object["network"]; + ASSERT_TRUE(network.isObject()); + ASSERT_EQ(2, static_cast<int>(network.size())); + ASSERT_EQ(9999, network["port"].toInt()); + ASSERT_EQ("localhost", network["host"].toString()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +/* + * Array. + * ------------------------------------------------------------------ + */ + +TEST(Array, push) +{ + try { + json::Value array(json::Type::Array); + + ASSERT_TRUE(array.isArray()); + + array.push(1); + array.push("hello"); + array.push(true); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_TRUE(array[0].toBool()); + ASSERT_EQ("hello", array[1].toString()); + ASSERT_EQ(1, array[2].toInt()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, append) +{ + try { + json::Value array(json::Type::Array); + + ASSERT_TRUE(array.isArray()); + + array.append(1); + array.append("hello"); + array.append(true); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_EQ(1, array[0].toInt()); + ASSERT_EQ("hello", array[1].toString()); + ASSERT_TRUE(array[2].toBool()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, insert) +{ + try { + json::Value array(json::Type::Array); + + ASSERT_TRUE(array.isArray()); + + array.insert(0, 1); + array.insert(1, "hello"); + array.insert(0, true); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_TRUE(array[0].toBool()); + ASSERT_EQ(1, array[1].toInt()); + ASSERT_EQ("hello", array[2].toString()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, clear) +{ + try { + json::Value array(json::Type::Array); + + array.append(1); + array.append("hello"); + array.append(true); + + array.clear(); + + ASSERT_EQ(0, static_cast<int>(array.size())); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, erase) +{ + try { + json::Value array(json::Type::Array); + + array.append(1); + array.append("hello"); + array.append(true); + + array.erase(0); + + ASSERT_EQ(2, static_cast<int>(array.size())); + ASSERT_EQ("hello", array[0].toString()); + ASSERT_TRUE(array[1].toBool()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, valueOr) +{ + try { + json::Value array = json::array({-10, -20}); + + ASSERT_EQ(-10, array.valueOr(0, -9999).toInt()); + ASSERT_EQ(-20, array.valueOr(1, -9999).toInt()); + ASSERT_EQ(-9999, array.valueOr(2, -9999).toInt()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(Array, valueOr2) +{ + try { + json::Value array = json::array({-10, -20}); + + ASSERT_EQ(-10, array.valueOr(0, json::Type::Int, -9999).toInt()); + ASSERT_EQ(-20, array.valueOr(1, json::Type::Int, -9999).toInt()); + ASSERT_EQ(-9999, array.valueOr(0, json::Type::String, -9999).toInt()); + ASSERT_EQ(-9999, array.valueOr(1, json::Type::Boolean, -9999).toInt()); + ASSERT_EQ(-9999, array.valueOr(2, -9999).toInt()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(ArrayInitializer, simple) +{ + try { + json::Value array = json::array({123, true, "hello"}); + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_EQ(123, array[0].toInt()); + ASSERT_TRUE(array[1].toBool()); + ASSERT_EQ("hello", array[2].toString()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(ArrayInitializer, deep) +{ + try { + json::Value array = json::array({ + 123, + true, + "hello", + json::array({ + 321, + false, + "olleh" + }) + }); + + // First. + ASSERT_EQ(4, static_cast<int>(array.size())); + ASSERT_EQ(123, array[0].toInt()); + ASSERT_TRUE(array[1].toBool()); + ASSERT_EQ("hello", array[2].toString()); + + // Second. + const json::Value &array2 = array[3]; + ASSERT_TRUE(array.isArray()); + ASSERT_EQ(3, static_cast<int>(array2.size())); + ASSERT_EQ(321, array2[0].toInt()); + ASSERT_FALSE(array2[1].toBool()); + ASSERT_EQ("olleh", array2[2].toString()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +/* + * I/O. + * ------------------------------------------------------------------ + */ + +TEST(FileRead, simple) +{ + try { + json::Value doc = json::fromFile(DIRECTORY "simple.json"); + + ASSERT_TRUE(doc.isObject()); + ASSERT_FALSE(doc.isArray()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(FileRead, fail) +{ + ASSERT_THROW(json::fromFile(DIRECTORY "notexist.json"), json::Error); +} + +TEST(StringRead, simple) +{ + try { + json::Value doc = json::fromString("{ \"license\": \"ISC\" }"); + + ASSERT_TRUE(doc.isObject()); + ASSERT_FALSE(doc.isArray()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST(StringRead, fail) +{ + ASSERT_THROW(json::fromString("{ \"license\": ISC }"), json::Error); +} + +/* + * Object read. + * ------------------------------------------------------------------ + */ + +class ObjectRead : public testing::Test { +protected: + json::Value m_object; + json::Value m_objectAll; + +public: + ObjectRead() + : m_object(json::fromFile(DIRECTORY "object.json")) + , m_objectAll(json::fromFile(DIRECTORY "object-all.json")) + { + } +}; + +TEST_F(ObjectRead, simple) +{ + try { + json::Value name = m_object["name"]; + json::Value description = m_object["description"]; + + ASSERT_TRUE(name.isString()); + ASSERT_TRUE(description.isString()); + ASSERT_EQ("simple", name.toString()); + ASSERT_EQ("basic JSON file", description.toString()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(ObjectRead, all) +{ + try { + ASSERT_TRUE(m_objectAll.contains("integer")); + ASSERT_FALSE(m_objectAll.contains("unexistant")); + + ASSERT_TRUE(m_objectAll["integer"].isInt()); + ASSERT_TRUE(m_objectAll["integer"].isNumber()); + ASSERT_EQ(123, m_objectAll["integer"].toInt()); + + ASSERT_TRUE(m_objectAll["real"].isNumber()); + ASSERT_TRUE(m_objectAll["real"].isNumber()); + ASSERT_EQ(9.2, m_objectAll["real"].toReal()); + + ASSERT_TRUE(m_objectAll["false"].isBool()); + ASSERT_FALSE(m_objectAll["false"].toBool()); + ASSERT_FALSE(m_objectAll["false"].toBool()); + + ASSERT_TRUE(m_objectAll["true"].isBool()); + ASSERT_TRUE(m_objectAll["true"].toBool()); + ASSERT_TRUE(m_objectAll["true"].toBool()); + + ASSERT_TRUE(m_objectAll["null"].isNull()); + + ASSERT_TRUE(m_objectAll["object"].isObject()); + ASSERT_TRUE(m_objectAll["array"].isArray()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +/* + * Array read. + * ------------------------------------------------------------------ + */ + +class ArrayRead : public testing::Test { +protected: + json::Value m_array; + json::Value m_arrayAll; + +public: + ArrayRead() + : m_array(json::fromFile(DIRECTORY "array.json")) + , m_arrayAll(json::fromFile(DIRECTORY "array-all.json")) + { + } +}; + +TEST_F(ArrayRead, simple) +{ + try { + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(1, m_array[0].toInt()); + ASSERT_EQ(2, m_array[1].toInt()); + ASSERT_EQ(3, m_array[2].toInt()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(ArrayRead, all) +{ + try { + ASSERT_TRUE(m_arrayAll[0].isInt()); + ASSERT_TRUE(m_arrayAll[0].isNumber()); + ASSERT_EQ(123, m_arrayAll[0].toInt()); + + ASSERT_TRUE(m_arrayAll[1].isNumber()); + ASSERT_TRUE(m_arrayAll[1].isNumber()); + ASSERT_EQ(9.2, m_arrayAll[1].toReal()); + + ASSERT_TRUE(m_arrayAll[2].isBool()); + ASSERT_FALSE(m_arrayAll[2].toBool()); + ASSERT_FALSE(m_arrayAll[2].toBool()); + + ASSERT_TRUE(m_arrayAll[3].isBool()); + ASSERT_TRUE(m_arrayAll[3].toBool()); + ASSERT_TRUE(m_arrayAll[3].toBool()); + + ASSERT_TRUE(m_arrayAll[4].isNull()); + + ASSERT_TRUE(m_arrayAll[5].isObject()); + ASSERT_TRUE(m_arrayAll[6].isArray()); + } catch (const json::Error &ex) { + FAIL() << ex.what(); + } +} + +/* + * Object iterators. + * ------------------------------------------------------------------ + */ + +class ObjectIteratorsTest : public testing::Test { +protected: + json::Value m_object{json::Type::Object}; + +public: + ObjectIteratorsTest() + { + m_object.insert("integer", 1); + m_object.insert("string", "hello"); + m_object.insert("boolean", true); + } +}; + +TEST_F(ObjectIteratorsTest, operators) +{ + // Read only (non const). + { + std::set<std::string> expected{"boolean", "integer", "string"}; + std::set<std::string> result; + std::unordered_map<std::string, json::Value> values; + auto it = m_object.begin(); + auto end = m_object.end(); + + while (it != end) { + values.insert({it.key(), *it}); + result.insert(it.key()); + it++; + } + + ASSERT_EQ(expected, result); + ASSERT_EQ(1, values["integer"].toInt()); + ASSERT_EQ("hello", values["string"].toString()); + ASSERT_TRUE(values["boolean"].toBool()); + } + + // Read only (const). + { + std::set<std::string> expected{"boolean", "integer", "string"}; + std::set<std::string> result; + std::unordered_map<std::string, json::Value> values; + auto it = m_object.cbegin(); + auto end = m_object.cend(); + + while (it != end) { + values.insert({it.key(), *it}); + result.insert(it.key()); + it++; + } + + ASSERT_EQ(expected, result); + ASSERT_EQ(1, values["integer"].toInt()); + ASSERT_EQ("hello", values["string"].toString()); + ASSERT_TRUE(values["boolean"].toBool()); + } +} + +TEST_F(ObjectIteratorsTest, assign) +{ + // Assign (non const). + { + auto it = m_object.begin(); + auto key = it.key(); + + *it = json::Value("CHANGED"); + + ASSERT_EQ("CHANGED", m_object[key].toString()); + ASSERT_EQ("CHANGED", it->toString()); + ASSERT_EQ(3, static_cast<int>(m_object.size())); + } +} + +TEST_F(ObjectIteratorsTest, find) +{ + auto it = m_object.find("integer"); + + ASSERT_TRUE(it != m_object.end()); + ASSERT_EQ(1, it->toInt()); +} + +TEST_F(ObjectIteratorsTest, find2) +{ + auto it = m_object.find("not exists"); + + ASSERT_TRUE(it == m_object.end()); +} + +/* + * Array iterators. + * ------------------------------------------------------------------ + */ + +class ArrayIteratorsTest : public testing::Test { +protected: + json::Value m_array{json::Type::Array}; + +public: + ArrayIteratorsTest() + { + m_array.append(1); + m_array.append("hello"); + m_array.append(true); + } +}; + +TEST_F(ArrayIteratorsTest, operators) +{ + // Read only (non const). + { + auto it = m_array.begin(); + + // 1. + ASSERT_EQ(1, (*it).toInt()); + ASSERT_EQ(1, it->toInt()); + + // "hello". + it++; + ASSERT_EQ("hello", it->toString()); + + // true. + it++; + ASSERT_TRUE(it->toBool()); + } + + // Read only (const). + { + auto it = m_array.cbegin(); + + // 1 + ASSERT_EQ(1, (*it).toInt()); + ASSERT_EQ(1, it->toInt()); + + // "hello". + it++; + ASSERT_EQ("hello", it->toString()); + + // true. + it++; + ASSERT_TRUE(it->toBool()); + } +} + +TEST_F(ArrayIteratorsTest, assign) +{ + // Assign (non const). + { + auto it = m_array.begin(); + + *it = json::Value(9999); + + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(9999, it->toInt()); + ASSERT_EQ(9999, m_array[0].toInt()); + } +} + +TEST_F(ArrayIteratorsTest, castToRef) +{ + json::Value array = json::array({1, 2, 3}); + int i = 1; + + for (const json::Value &v : array) { + ASSERT_EQ(i++, v.toInt()); + } +} + +/* + * Value::toJson. + * ------------------------------------------------------------------ + */ + +TEST(ToJsonCompact, simpleObject) +{ + json::Value v = json::object({ { "x", 10 }, { "y", 20 } }); + std::string result = v.toJson(0); + std::string expected = "{\"x\":10,\"y\":20}"; + + ASSERT_EQ(expected, result); +} + +TEST(ToJsonCompact, deepObject) +{ + json::Value v = json::object({ + { "x", 10 }, + { "y", 20 }, + { "network", json::object({ + { "host", "localhost" } + })} + }); + + std::string result = v.toJson(0); + std::string expected = "{\"network\":{\"host\":\"localhost\"},\"x\":10,\"y\":20}"; + + ASSERT_EQ(expected, result); +} + +TEST(ToJsonCompact, simpleArray) +{ + json::Value v = json::array({ 1, 2, 3 }); + std::string result = v.toJson(0); + std::string expected = "[1,2,3]"; + + ASSERT_EQ(expected, result); +} + +TEST(ToJsonCompact, deepArray) +{ + json::Value v = json::array({ + 1, + 2, + json::array({ 4, 5, 6 }) + }); + std::string result = v.toJson(0); + std::string expected = "[1,2,[4,5,6]]"; + + ASSERT_EQ(expected, result); +} + +TEST(ToJsonIndentSpaces, object) +{ + json::Value v = json::object({ + { "x", 10 }, + { "y", 20 }, + { "network", json::object({ + { "host", "localhost" } + })} + }); + std::string result = v.toJson(2); + std::string expected = "{\n \"network\": {\n \"host\": \"localhost\"\n },\n \"x\": 10,\n \"y\": 20\n}"; + + ASSERT_EQ(expected, result); +} + +TEST(ToJsonIndentSpaces, array) +{ + json::Value v = json::array({ + 1, + 2, + json::object({ { "x", 10 } }) + }); + std::string result = v.toJson(); + std::string expected = "[\n 1,\n 2,\n {\n \"x\": 10\n }\n]"; + + ASSERT_EQ(expected, result); +} + +/* + * Value::toString with coerce or not. + * ------------------------------------------------------------------ + */ + +TEST(ToString, boolean) +{ + { + json::Value value(true); + ASSERT_EQ("", value.toString()); + ASSERT_EQ("true", value.toString(true)); + } + + { + json::Value value(false); + ASSERT_EQ("", value.toString()); + ASSERT_EQ("false", value.toString(true)); + } +} + +TEST(ToString, integer) +{ + json::Value value(123); + ASSERT_EQ("", value.toString()); + ASSERT_EQ("123", value.toString(true)); +} + +TEST(ToString, null) +{ + json::Value value(nullptr); + ASSERT_EQ("", value.toString()); + ASSERT_EQ("null", value.toString(true)); +} + +TEST(ToString, real) +{ + json::Value value(20.5); + ASSERT_EQ("", value.toString()); + ASSERT_EQ("20.5", value.toString(true)); +} + +TEST(ToString, string) +{ + json::Value value("hello"); + ASSERT_EQ("hello", value.toString()); + ASSERT_EQ("hello", value.toString(true)); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}