From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/third_party/json-c/AUTHORS | 61 + comm/third_party/json-c/COPYING | 42 + comm/third_party/json-c/ChangeLog | 583 +++++++ comm/third_party/json-c/INSTALL | 2 + comm/third_party/json-c/NEWS | 1 + comm/third_party/json-c/README.html | 41 + comm/third_party/json-c/README.md | 300 ++++ comm/third_party/json-c/RELEASE_CHECKLIST.txt | 179 ++ comm/third_party/json-c/STYLE.txt | 31 + comm/third_party/json-c/arraylist.c | 205 +++ comm/third_party/json-c/arraylist.h | 88 + comm/third_party/json-c/config.h.in | 217 +++ comm/third_party/json-c/debug.c | 96 ++ comm/third_party/json-c/debug.h | 98 ++ comm/third_party/json-c/json.h.cmakein | 38 + comm/third_party/json-c/json_c_version.c | 19 + comm/third_party/json-c/json_c_version.h | 55 + comm/third_party/json-c/json_config.h.in | 3 + comm/third_party/json-c/json_inttypes.h | 24 + comm/third_party/json-c/json_object.c | 1808 ++++++++++++++++++++ comm/third_party/json-c/json_object.h | 1077 ++++++++++++ comm/third_party/json-c/json_object_iterator.c | 153 ++ comm/third_party/json-c/json_object_iterator.h | 228 +++ comm/third_party/json-c/json_object_private.h | 107 ++ comm/third_party/json-c/json_pointer.c | 358 ++++ comm/third_party/json-c/json_pointer.h | 123 ++ comm/third_party/json-c/json_tokener.c | 1300 ++++++++++++++ comm/third_party/json-c/json_tokener.h | 328 ++++ comm/third_party/json-c/json_types.h | 78 + comm/third_party/json-c/json_util.c | 296 ++++ comm/third_party/json-c/json_util.h | 121 ++ comm/third_party/json-c/json_visit.c | 128 ++ comm/third_party/json-c/json_visit.h | 101 ++ comm/third_party/json-c/libjson.c | 26 + comm/third_party/json-c/linkhash.c | 716 ++++++++ comm/third_party/json-c/linkhash.h | 447 +++++ comm/third_party/json-c/math_compat.h | 43 + comm/third_party/json-c/moz.build | 52 + comm/third_party/json-c/printbuf.c | 180 ++ comm/third_party/json-c/printbuf.h | 131 ++ comm/third_party/json-c/random_seed.c | 355 ++++ comm/third_party/json-c/random_seed.h | 29 + comm/third_party/json-c/snprintf_compat.h | 41 + comm/third_party/json-c/strdup_compat.h | 16 + comm/third_party/json-c/strerror_override.c | 110 ++ comm/third_party/json-c/strerror_override.h | 30 + .../third_party/json-c/strerror_override_private.h | 14 + comm/third_party/json-c/vasprintf_compat.h | 67 + 48 files changed, 10546 insertions(+) create mode 100644 comm/third_party/json-c/AUTHORS create mode 100644 comm/third_party/json-c/COPYING create mode 100644 comm/third_party/json-c/ChangeLog create mode 100644 comm/third_party/json-c/INSTALL create mode 100644 comm/third_party/json-c/NEWS create mode 100644 comm/third_party/json-c/README.html create mode 100644 comm/third_party/json-c/README.md create mode 100644 comm/third_party/json-c/RELEASE_CHECKLIST.txt create mode 100644 comm/third_party/json-c/STYLE.txt create mode 100644 comm/third_party/json-c/arraylist.c create mode 100644 comm/third_party/json-c/arraylist.h create mode 100644 comm/third_party/json-c/config.h.in create mode 100644 comm/third_party/json-c/debug.c create mode 100644 comm/third_party/json-c/debug.h create mode 100644 comm/third_party/json-c/json.h.cmakein create mode 100644 comm/third_party/json-c/json_c_version.c create mode 100644 comm/third_party/json-c/json_c_version.h create mode 100644 comm/third_party/json-c/json_config.h.in create mode 100644 comm/third_party/json-c/json_inttypes.h create mode 100644 comm/third_party/json-c/json_object.c create mode 100644 comm/third_party/json-c/json_object.h create mode 100644 comm/third_party/json-c/json_object_iterator.c create mode 100644 comm/third_party/json-c/json_object_iterator.h create mode 100644 comm/third_party/json-c/json_object_private.h create mode 100644 comm/third_party/json-c/json_pointer.c create mode 100644 comm/third_party/json-c/json_pointer.h create mode 100644 comm/third_party/json-c/json_tokener.c create mode 100644 comm/third_party/json-c/json_tokener.h create mode 100644 comm/third_party/json-c/json_types.h create mode 100644 comm/third_party/json-c/json_util.c create mode 100644 comm/third_party/json-c/json_util.h create mode 100644 comm/third_party/json-c/json_visit.c create mode 100644 comm/third_party/json-c/json_visit.h create mode 100644 comm/third_party/json-c/libjson.c create mode 100644 comm/third_party/json-c/linkhash.c create mode 100644 comm/third_party/json-c/linkhash.h create mode 100644 comm/third_party/json-c/math_compat.h create mode 100644 comm/third_party/json-c/moz.build create mode 100644 comm/third_party/json-c/printbuf.c create mode 100644 comm/third_party/json-c/printbuf.h create mode 100644 comm/third_party/json-c/random_seed.c create mode 100644 comm/third_party/json-c/random_seed.h create mode 100644 comm/third_party/json-c/snprintf_compat.h create mode 100644 comm/third_party/json-c/strdup_compat.h create mode 100644 comm/third_party/json-c/strerror_override.c create mode 100644 comm/third_party/json-c/strerror_override.h create mode 100644 comm/third_party/json-c/strerror_override_private.h create mode 100644 comm/third_party/json-c/vasprintf_compat.h (limited to 'comm/third_party/json-c') diff --git a/comm/third_party/json-c/AUTHORS b/comm/third_party/json-c/AUTHORS new file mode 100644 index 0000000000..b36fa9b347 --- /dev/null +++ b/comm/third_party/json-c/AUTHORS @@ -0,0 +1,61 @@ +Alan Coopersmith +Alexander Dahl +Alexandru Ardelean +andy5995 +Aram Poghosyan +Björn Esser +BonsaY +changyong guo +chenguoping +Chris Lamb +Christopher Head +Chris Wolfe +C. Watford (christopher.watford@gmail.com) +Darjan Krijan +David McCann +DeX77 +dota17 +Eric Haszlakiewicz +Eric Hawicz +Even Rouault +Gianluigi Tiesi +grdowns +Hex052 +hofnarr +ihsinme <61293369+ihsinme@users.noreply.github.com> +Ivan Romanov +Jaap Keuter +Jakov Smolic +janczer +Jehan +Jehiah Czebotar +Jonathan Wiens +Jose Bollo +José Bollo +Juuso Alasuutari +Keith Holman +Kizuna-Meraki +Leon Gross +Liang, Gao +Marc <34656315+MarcT512@users.noreply.github.com> +max +Micah Snyder +Michael Clark +myd7349 +Pascal Cuoq +Pawday +Philosoph228 +Pierce Lopez +Po-Chuan Hsieh +Ramiro Polla +Rikard Falkeborn +Robert Bielik +Robert +Rosen Penev +Rubasri Kalidas +Simon McVittie +ssrlive <30760636+ssrlive@users.noreply.github.com> +Tobias Nießen +Tobias Stoeckmann +Tudor Brindus +Unmanned Player <36690541+unmanned-player@users.noreply.github.com> diff --git a/comm/third_party/json-c/COPYING b/comm/third_party/json-c/COPYING new file mode 100644 index 0000000000..740d1258d4 --- /dev/null +++ b/comm/third_party/json-c/COPYING @@ -0,0 +1,42 @@ + +Copyright (c) 2009-2012 Eric Haszlakiewicz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------------------------------------- + +Copyright (c) 2004, 2005 Metaparadigm Pte Ltd + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/comm/third_party/json-c/ChangeLog b/comm/third_party/json-c/ChangeLog new file mode 100644 index 0000000000..b03a77710e --- /dev/null +++ b/comm/third_party/json-c/ChangeLog @@ -0,0 +1,583 @@ + +0.16 (up to commit 66dcdf5, 2022-04-13) +======================================== + +Deprecated and removed features: +-------------------------------- +* JSON_C_OBJECT_KEY_IS_CONSTANT is deprecated in favor of + JSON_C_OBJECT_ADD_CONSTANT_KEY +* Direct access to lh_table and lh_entry structure members is deprecated. + Use access functions instead, lh_table_head(), lh_entry_next(), etc... +* Drop REFCOUNT_DEBUG code. + +New features +------------ +* The 0.16 release introduces no new features + +Build changes +------------- +* Add a DISABLE_EXTRA_LIBS option to skip using libbsd +* Add a DISABLE_JSON_POINTER option to skip compiling in json_pointer support. + +Significant changes and bug fixes +--------------------------------- +* Cap string length at INT_MAX to avoid various issues with very long strings. +* json_object_deep_copy: fix deep copy of strings containing '\0' +* Fix read past end of buffer in the "json_parse" command +* Avoid out of memory accesses in the locally provided vasprintf() function + (for those platforms that use it) +* Handle allocation failure in json_tokener_new_ex +* Fix use-after-free in json_tokener_new_ex() in the event of printbuf_new() returning NULL +* printbuf_memset(): set gaps to zero - areas within the print buffer which + have not been initialized by using printbuf_memset +* printbuf: return -1 on invalid arguments (len < 0 or total buffer > INT_MAX) +* sprintbuf(): propagate printbuf_memappend errors back to the caller + +Optimizations +-------------- +* Speed up parsing by replacing ctype functions with simplified, faster + non-locale-sensitive ones in json_tokener and json_object_to_json_string. +* Neither vertical tab nor formfeed are considered whitespace per the JSON spec +* json_object: speed up creation of objects, calloc() -> malloc() + set fields +* Avoid needless extra strlen() call in json_c_shallow_copy_default() and + json_object_equal() when the object is known to be a json_type_string. + +Other changes +------------- +* Validate size arguments in arraylist functions. +* Use getrandom() if available; with GRND_NONBLOCK to allow use of json-c + very early during boot, such as part of cryptsetup. +* Use arc4random() if it's available. +* random_seed: on error, continue to next method instead of exiting the process +* Close file when unable to read from /dev/urandom in get_dev_random_seed() + +*** + +0.15 (up to commit 870965e, 2020/07/26) +======================================== + +Deprecated and removed features: +-------------------------------- +* Deprecate `array_list_new()` in favor of `array_list_new2()` +* Remove the THIS_FUNCTION_IS_DEPRECATED define. +* Remove config.h.win32 + +New features +------------ +* Add a `JSON_TOKENER_ALLOW_TRAILING_CHARS` flag to allow multiple objects + to be parsed even when `JSON_TOKENER_STRICT` is set. +* Add `json_object_new_array_ext(int)` and `array_list_new_2(int)` to allow + arrays to be allocated with the exact size needed, when known. +* Add `json_object_array_shrink()` (and `array_list_shrink()`) and use it in + json_tokener to minimize the amount of memory used. +* Add a json_parse binary, for use in testing changes (not installed, but + available in the apps directory). + +Build changes +------------- +* #639/#621 - Add symbol versions to all exported symbols +* #508/#634 - Always enable -fPIC to allow use of the json-c static library in + other libraries +* Build both static and shared libraries at the same time. +* #626 - Restore compatibility with cmake 2.8 +* #471 - Always create directories with mode 0755, regardless of umask. +* #606/#604 - Improve support for OSes like AIX and IBM i, as well as for + MINGW32 and old versions of MSVC +* #451/#617 - Add a DISABLE_THREAD_LOCAL_STORAGE cmake option to disable + the use of thread-local storage. + +Significant changes and bug fixes +--------------------------------- +* Split the internal json_object structure into several sub-types, one for + each json_type (json_object_object, json_object_string, etc...). + This improves memory usage and speed, with the benchmark under + bench/ report 5.8% faster test time and 6%(max RSS)-12%(peak heap) + less memory usage. + Memory used just for json_object structures decreased 27%, so use cases + with fewer arrays and/or strings would benefit more. +* Minimize memory usage in array handling in json_tokener by shrinking + arrays to the exact number of elements parsed. On bench/ benchmark: + 9% faster test time, 39%(max RSS)-50%(peak heap) less memory usage. + Add json_object_array_shrink() and array_list_shrink() functions. +* #616 - Parsing of surrogate pairs in unicode escapes now properly handles + incremental parsing. +* Fix incremental parsing of numbers, especially those with exponents, e.g. + so parsing "[0", "e+", "-]" now properly returns an error. + Strict mode now rejects missing exponents ("0e"). +* Successfully return number objects at the top level even when they are + followed by a "-", "." or "e". This makes parsing things like "123-45" + behave consistently with things like "123xyz". + +Other changes +------------- +* #589 - Detect broken RDRAND during initialization; also, fix segfault + in the CPUID check. +* #592 - Fix integer overflows to prevert out of bounds write on large input. +* Protect against division by zero in linkhash, when created with zero size. +* #602 - Fix json_parse_uint64() internal error checking, leaving the retval + untouched in more failure cases. +* #614 - Prevent truncation when custom double formatters insert extra \0's + + +*** + +0.14 (up to commit 9ed00a6, 2020/04/14) +========================================= + +Deprecated and removed features: +-------------------------------- +* bits.h has been removed +* lh_abort() has been removed +* lh_table_lookup() has been removed, use lh_table_lookup_ex() instead. +* Remove TRUE and FALSE defines, use 1 and 0 instead. + +Build changes: +-------------- +## Deprecated and removed features: +* bits.h has been removed +* lh_abort() has been removed +* lh_table_lookup() has been removed, use lh_table_lookup_ex() instead. +* Remove TRUE and FALSE defines, use 1 and 0 instead. +* autoconf support, including autogen.sh, has been removed. See details about cmake, below. +* With the addition of json_tokener_get_parse_end(), access to internal fields of json_tokener, as well as use of many other symbols and types in json_tokener.h, is deprecated now. +* The use of Android.configure.mk to build for Android no longer works, and it is unknown how (or if) the new cmake-based build machinery can be used. + * Reports of success, or pull requests to correct issues are welcome. + +## Notable improvements and new features + +### Builds and documentation +* Build machinery has been switched to CMake. See README.md for details about how to build. + * TL;DR: `mkdir build ; cd build ; cmake -DCMAKE_INSTALL_PREFIX=/some/path ../json-c ; make all test install` + * To ease the transition, there is a `cmake-configure` wrapper that emulates the old autoconf-based configure script. + * This has enabled improvements to the build on Windows system; also all public functions have been fixed to be properly exported. For best results, use Visual Studio 2015 or newer. +* The json-c style guide has been updated to specify the use of clang-format, and all code has been reformatted. + * Since many lines of code have trivial changes now, when using git blame, be sure to specify -w +* Numerous improvements have been made to the documentation including function effects on refcounts, when passing a NULL is safe, and so on. + +### json_tokener changes +* Added a json_tokener_get_parse_end() function to replace direct access of tok->char_offset. + * The char_offset field, and the rest of the json_tokener structure remain exposed for now, but expect a future release to hide it like is done with json_object_private.h +* json_tokener_parse_ex() now accepts a new JSON_TOKENER_VALIDATE_UTF8 flag to validate that input is UTF8. + * If validation fails, json_tokener_get_error(tok) will return json_tokener_error_parse_utf8_string (see enum json_tokener_error). + +### Other changes and additions +* Add support for unsigned 64-bit integers, uint64_t, to gain one extra bit of magnitude for positive ints. + * json_tokener will now parse values up to UINT64_MAX (18446744073709551615) + * Existing methods returning int32_t or int64_t will cap out-of-range values at INT32_MAX or INT64_MAX, preserving existing behavior. + * The implementation includes the possibility of easily extending this to larger sizes in the future. +* A total of 7 new functions were added: + * json_object_get_uint64 ( struct json_object const* jso ) + * json_object_new_uint64 ( uint64_t i ) + * json_object_set_uint64 ( struct json_object* jso, uint64_t new_value ) + * json_parse_uint64 ( char const* buf, uint64_t* retval ) + * See description of uint64 support, above. + * json_tokener_get_parse_end ( struct json_tokener* tok ) + * See details under "json_tokener changes", above. + * json_object_from_fd_ex ( int fd, int in_depth ) + * Allows the max nesting depth to be specified. + * json_object_new_null ( ) + * Simply returns NULL. Its use is not recommended. +* The size of struct json_object has decreased from 96 bytes to 88 bytes. + +### Testing +* Many updates were made to test cases, increasing code coverage. +* There is now a quick way (JSONC_TEST_TRACE=1) to turn on shell tracing in tests. +* To run tests, use `make test`; the old "check" target no longer exists. + +## Significant bug fixes +For the full list of issues and pull requests since the previous release, please see issues_closed_for_0.14.md + +* [Issue #389](https://github.com/json-c/json-c/issues/389): Add an assert to explicitly crash when _ref_count is corrupted, instead of a later "double free" error. +* [Issue #407](https://github.com/json-c/json-c/issues/407): fix incorrect casts in calls to ctype functions (isdigit and isspace) so we don't crash when asserts are enabled on certain platforms and characters > 128 are parsed. +* [Issue #418](https://github.com/json-c/json-c/issues/418): Fix docs for json_util_from_fd and json_util_from_file to say that they return NULL on failures. +* [Issue #422](https://github.com/json-c/json-c/issues/422): json_object.c:set errno in json_object_get_double() when called on a json_type_string object with bad content. +* [Issue #453](https://github.com/json-c/json-c/issues/453): Fixed misalignment in JSON serialization when JSON_C_TO_STRING_SPACED and JSON_C_TO_STRING_PRETTY are used together. +* [Issue #463](https://github.com/json-c/json-c/issues/463): fix newlocale() call to use LC_NUMERIC_MASK instead of LC_NUMERIC, and remove incorrect comment. +* [Issue #486](https://github.com/json-c/json-c/issues/486): append a missing ".0" to negative double values to ensure they are serialized as floating point numbers. +* [Issue #488](https://github.com/json-c/json-c/issues/488): use JSON_EXPORT on functions so they are properly exported on Windows. +* [Issue #539](https://github.com/json-c/json-c/issues/539): use an internal-only serializer function in json_object_new_double_s() to avoid potential conflicts with user code that uses the json_object_userdata_to_json_string serializer. + +*** + +0.13.1 (up to commit 0f814e5, 2018/03/04) +========================================= + +* Bump the major version of the .so library generated up to 4.0 to avoid + conflicts because some downstream packagers of json-c had already done + their own bump to ".so.3" for a much older 0.12 release. +* Add const size_t json_c_object_sizeof() +* Avoid invalid free (and thus a segfault) when ref_count gets < 0 +* PR#394: fix handling of custom double formats that include a ".0" +* Avoid uninitialized variable warnings in json_object_object_foreach +* Issue #396: fix build for certain uClibc based systems. +* Add a top level fuzz directory for fuzzers run by OSS-Fuzz + + +0.13 (up to commit 5dae561, 2017/11/29) +================================= + +This release, being three and a half years after the 0.12 branch (f84d9c), + has quite a number of changes included. The following is a sampling of + the most significant ones. + +Since the 0.12 release, 250 issues and pull requests have been closed. +See issues_closed_for_0.13.md for a complete list. + + +Deprecated and removed features: +-------------------------------- +* All internal use of bits.h has been eliminated. The file will be removed. + Do not use: hexdigit(), error_ptr(), error_descrition() and it_error() +* lh_abort() is deprecated. It will be removed. + +Behavior changes: +----------------- +* Tighten the number parsing algorithm to raise errors instead of truncating + the results. For example 12.3.4 or 2015-01-15, which now return null. + See commit 99d8fc + +* Use size_t for array length and size. Platforms where sizeof(size_t) != sizeof(int) may not be backwards compatible + See commits 45c56b, 92e9a5 and others. + +* Check for failure when allocating memory, returning NULL and errno=ENOMEM. + See commit 2149a04. + +* Change json_object_object_add() return type from void to int, and will return -1 on failures, instead of exiting. (Note: this is not an ABI change) + +New features: +------------- +* We're aiming to follow RFC 7159 now. + +* Add a couple of additional option to json_object_to_json_string_ext: + JSON_C_TO_STRING_PRETTY_TAB + JSON_C_TO_STRING_NOSLASHESCAPE + +* Add a json_object_object_add_ex() function to allow for performance + improvements when certain constraints are known to be true. + +* Make serialization format of doubles configurable, in two different ways: + Call json_object_set_serializer with json_object_double_to_json_string and a custom + format on each double object, or + Call json_c_set_serialization_double_format() to set a global or thread-wide format. + +* Add utility function for comparing json_objects - json_object_equal() + +* Add a way to copy entire object trees: json_object_deep_copy() +* Add json_object_set_ function to modify the value of existing json_object's + without the need to recreate them. Also add a json_object_int_inc function to + adjust an int's value. +* Add support for JSON pointer, RFC 6901. See json_pointer.h +* Add a json_util_get_last_err() function to retrieve the string describing the + cause of errors, instead of printing to stderr. +* Add perllike hash function for strings, and json_global_set_string_hash() 8f8d03d +* Add a json_c_visit() function to provide a way to iterate over a tree of json-c objects. + +Notable bug fixes and other improvements: +----------------------------------------- +* Make reference increment and decrement atomic to allow passing json objects between threads. +* Fix json_object_object_foreach to avoid uninitialized variable warnings. +* Improve performance by removing unneeded data items from hashtable code and reducing duplicate hash computation. +* Improve performance by storing small strings inside json_object +* Improve performance of json_object_to_json_string by removing variadic printf. commit 9ff0f49 +* Issue #371: fix parsing of "-Infinity", and avoid needlessly copying the input when doing so. +* Fix stack buffer overflow in json_object_double_to_json_string_format() - commit 2c2deb87 +* Fix various potential null ptr deref and int32 overflows +* Issue #332: fix a long-standing bug in array_list_put_idx() where it would attempt to free previously free'd entries due to not checking the current array length. +* Issue #195: use uselocale() instead of setlocale() in json_tokener to behave better in threaded environments. +* Issue #275: fix out of bounds read when handling unicode surrogate pairs. +* Ensure doubles that happen to be a whole number are emitted with ".0" - commit ca7a19 +* PR#331: for Visual Studio, use a snprintf/vsnprintf wrapper that ensures the string is terminated. +* Fix double to int cast overflow in json_object_get_int64. +* Clamp double to int32 when narrowing in json_object_get_int. +* Use strtoll() to parse ints - instead of sscanf +* Miscellaneous smaller changes, including removing unused variables, fixing warning + about uninitialized variables adding const qualifiers, reformatting code, etc... + +Build changes: +-------------- +* Add Appveyor and Travis build support +* Switch to using CMake when building on Windows with Visual Studio. + A dynamic .dll is generated instead of a .lib + config.h is now generated, config.h.win32 should no longer be manually copied +* Add support for MacOS through CMake too. +* Enable silent build by default +* Link against libm when needed +* Add support for building with AddressSanitizer +* Add support for building with Clang +* Add a --enable-threading configure option, and only use the (slower) __sync_add_and_fetch()/__sync_sub_and_fetch() function when it is specified. + +List of new functions added: +---------------------------- +### json_object.h +* array_list_bsearch() +* array_list_del_idx() +* json_object_to_json_string_length() +* json_object_get_userdata() +* json_object_set_userdata() +* json_object_object_add_ex() +* json_object_array_bsearch() +* json_object_array_del_idx() +* json_object_set_boolean() +* json_object_set_int() +* json_object_int_inc() +* json_object_set_int64() +* json_c_set_serialization_double_format() +* json_object_double_to_json_string() +* json_object_set_double() +* json_object_set_string() +* json_object_set_string_len() +* json_object_equal() +* json_object_deep_copy() + +### json_pointer.h +* json_pointer_get() +* json_pointer_getf() +* json_pointer_set() +* json_pointer_setf() + +### json_util.h +* json_object_from_fd() +* json_object_to_fd() +* json_util_get_last_err() + +### json_visit.h +* json_c_visit() + +### linkhash.h +* json_global_set_string_hash() +* lh_table_resize() + +### printbuf.h +* printbuf_strappend() + + +0.12.1 +====== + + * Minimal changes to address compile issues. + +0.12 +==== + + * Address security issues: + * CVE-2013-6371: hash collision denial of service + * CVE-2013-6370: buffer overflow if size_t is larger than int + + * Avoid potential overflow in json_object_get_double + + * Eliminate the mc_abort() function and MC_ABORT macro. + + * Make the json_tokener_errors array local. It has been deprecated for + a while, and json_tokener_error_desc() should be used instead. + + * change the floating point output format to %.17g so values with + more than 6 digits show up in the output. + + * Remove the old libjson.so name compatibility support. The library is + only created as libjson-c.so now and headers are only installed + into the ${prefix}/json-c directory. + + * When supported by the linker, add the -Bsymbolic-functions flag. + + * Various changes to fix the build on MSVC. + + * Make strict mode more strict: + * number must not start with 0 + * no single-quote strings + * no comments + * trailing char not allowed + * only allow lowercase literals + + * Added a json_object_new_double_s() convenience function to allow + an exact string representation of a double to be specified when + creating the object and use it in json_tokener_parse_ex() so + a re-serialized object more exactly matches the input. + + * Add support NaN and Infinity + + +0.11 +==== + + * IMPORTANT: the name of the library has changed to libjson-c.so and + the header files are now in include/json-c. + The pkgconfig name has also changed from json to json-c. + You should change your build to use appropriate -I and -l options. + A compatibility shim is in place so builds using the old name will + continue to work, but that will be removed in the next release. + * Maximum recursion depth is now a runtime option. + json_tokener_new() is provided for compatibility. + json_tokener_new_ex(depth) + * Include json_object_iterator.h in the installed headers. + * Add support for building on Android. + * Rewrite json_object_object_add to replace just the value if the key already exists so keys remain valid. + * Make it safe to delete keys while iterating with the json_object_object_foreach macro. + * Add a json_set_serializer() function to allow the string output of a json_object to be customized. + * Make float parsing locale independent. + * Add a json_tokener_set_flags() function and a JSON_TOKENER_STRICT flag. + * Enable -Werror when building. + * speed improvements to parsing 64-bit integers on systems with working sscanf + * Add a json_object_object_length function. + * Fix a bug (buffer overrun) when expanding arrays to more than 64 entries. + +0.10 +==== + + * Add a json_object_to_json_string_ext() function to allow output to be + formatted in a more human readable form. + * Add json_object_object_get_ex(), a NULL-safe get object method, to be able + to distinguish between a key not present and the value being NULL. + * Add an alternative iterator implementation, see json_object_iterator.h + * Make json_object_iter public to enable external use of the + json_object_object_foreachC macro. + * Add a printbuf_memset() function to provide an efficient way to set and + append things like whitespace indentation. + * Adjust json_object_is_type and json_object_get_type so they return + json_type_null for NULL objects and handle NULL passed to + json_objct_object_get(). + * Rename boolean type to json_bool. + * Fix various compile issues for Visual Studio and MinGW. + * Allow json_tokener_parse_ex() to be re-used to parse multiple object. + Also, fix some parsing issues with capitalized hexadecimal numbers and + number in E notation. + * Add json_tokener_get_error() and json_tokener_error_desc() to better + encapsulate the process of retrieving errors while parsing. + * Various improvements to the documentation of many functions. + * Add new json_object_array_sort() function. + * Fix a bug in json_object_get_int(), which would incorrectly return 0 + when called on a string type object. + Eric Haszlakiewicz + * Add a json_type_to_name() function. + Eric Haszlakiewicz + * Add a json_tokener_parse_verbose() function. + Jehiah Czebotar + * Improve support for null bytes within JSON strings. + Jehiah Czebotar + * Fix file descriptor leak if memory allocation fails in json_util + Zachary Blair, zack_blair at hotmail dot com + * Add int64 support. Two new functions json_object_net_int64 and + json_object_get_int64. Binary compatibility preserved. + Eric Haszlakiewicz, EHASZLA at transunion com + Rui Miguel Silva Seabra, rms at 1407 dot org + * Fix subtle bug in linkhash where lookup could hang after all slots + were filled then successively freed. + Spotted by Jean-Marc Naud, j dash m at newtraxtech dot com + * Make json_object_from_file take const char *filename + Spotted by Vikram Raj V, vsagar at attinteractive dot com + * Add handling of surrogate pairs (json_tokener.c, test4.c, Makefile.am) + Brent Miller, bdmiller at yahoo dash inc dot com + * Correction to comment describing printbuf_memappend in printbuf.h + Brent Miller, bdmiller at yahoo dash inc dot com + +0.9 +=== + * Add README.html README-WIN32.html config.h.win32 to Makefile.am + Michael Clark, + * Add const qualifier to the json_tokener_parse functions + Eric Haszlakiewicz, EHASZLA at transunion dot com + * Rename min and max so we can never clash with C or C++ std library + Ian Atha, thatha at yahoo dash inc dot com + * Fix any noticeable spelling or grammar errors. + * Make sure every va_start has a va_end. + * Check all pointers for validity. + Erik Hovland, erik at hovland dot org + * Fix json_object_get_boolean to return false for empty string + Spotted by Vitaly Kruglikov, Vitaly dot Kruglikov at palm dot com + * optimizations to json_tokener_parse_ex(), printbuf_memappend() + Brent Miller, bdmiller at yahoo dash inc dot com + * Disable REFCOUNT_DEBUG by default in json_object.c + * Don't use this as a variable, so we can compile with a C++ compiler + * Add casts from void* to type of assignment when using malloc + * Add #ifdef __cplusplus guards to all of the headers + * Add typedefs for json_object, json_tokener, array_list, printbuf, lh_table + Michael Clark, + * Null pointer dereference fix. Fix json_object_get_boolean strlen test + to not return TRUE for zero length string. Remove redundant includes. + Erik Hovland, erik at hovland dot org + * Fixed warning reported by adding -Wstrict-prototypes + -Wold-style-definition to the compilatin flags. + Dotan Barak, dotanba at gmail dot com + * Add const correctness to public interfaces + Gerard Krol, g dot c dot krol at student dot tudelft dot nl + +0.8 +=== + * Add va_end for every va_start + Dotan Barak, dotanba at gmail dot com + * Add macros to enable compiling out debug code + Geoffrey Young, geoff at modperlcookbook dot org + * Fix bug with use of capital E in numbers with exponents + Mateusz Loskot, mateusz at loskot dot net + * Add stddef.h include + * Patch allows for json-c compile with -Werror and not fail due to + -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations + Geoffrey Young, geoff at modperlcookbook dot org + +0.7 +=== + * Add escaping of backslash to json output + * Add escaping of forward slash on tokenizing and output + * Changes to internal tokenizer from using recursion to + using a depth state structure to allow incremental parsing + +0.6 +=== + * Fix bug in escaping of control characters + Johan Björklund, johbjo09 at kth dot se + * Remove include "config.h" from headers (should only + be included from .c files) + Michael Clark + +0.5 +=== + * Make headers C++ compatible by change *this to *obj + * Add ifdef C++ extern "C" to headers + * Use simpler definition of min and max in bits.h + Larry Lansing, llansing at fuzzynerd dot com + + * Remove automake 1.6 requirement + * Move autogen commands into autogen.sh. Update README + * Remove error pointer special case for Windows + * Change license from LGPL to MIT + Michael Clark + +0.4 +=== + * Fix additional error case in object parsing + * Add back sign reversal in nested object parse as error pointer + value is negative, while error value is positive. + Michael Clark + +0.3 +=== + * fix pointer arithmetic bug for error pointer check in is_error() macro + * fix type passed to printbuf_memappend in json_tokener + * update autotools bootstrap instructions in README + Michael Clark + +0.2 +=== + * printbuf.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of vasprintf + * debug.c - C. Watford (christopher.watford@gmail.com) + Removed usage of vsyslog on Win32/Win64 systems, needs to be handled + by a configure script + * json_object.c - C. Watford (christopher.watford@gmail.com) + Added scope operator to wrap usage of json_object_object_foreach, this + needs to be rethought to be more ANSI C friendly + * json_object.h - C. Watford (christopher.watford@gmail.com) + Added Microsoft C friendly version of json_object_object_foreach + * json_tokener.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of strndup + * json_util.c - C. Watford (christopher.watford@gmail.com) + Added cast and mask to suffice size_t v. unsigned int conversion + correctness + * json_tokener.c - sign reversal issue on error info for nested object parse + spotted by Johan Björklund (johbjo09 at kth.se) + * json_object.c - escape " in json_escape_str + * Change to automake and libtool to build shared and static library + Michael Clark + +0.1 +=== + * initial release diff --git a/comm/third_party/json-c/INSTALL b/comm/third_party/json-c/INSTALL new file mode 100644 index 0000000000..d8575d3fb6 --- /dev/null +++ b/comm/third_party/json-c/INSTALL @@ -0,0 +1,2 @@ + +See README.md for installation instructions. diff --git a/comm/third_party/json-c/NEWS b/comm/third_party/json-c/NEWS new file mode 100644 index 0000000000..5798fb41b9 --- /dev/null +++ b/comm/third_party/json-c/NEWS @@ -0,0 +1 @@ +See the git repo. diff --git a/comm/third_party/json-c/README.html b/comm/third_party/json-c/README.html new file mode 100644 index 0000000000..483e4075b2 --- /dev/null +++ b/comm/third_party/json-c/README.html @@ -0,0 +1,41 @@ + + + + JSON-C - A JSON implementation in C + + + +

JSON-C - A JSON implementation in C

+ +

Overview

+

JSON-C implements a reference counting object model that allows you to easily + construct JSON objects in C, output them as JSON formatted strings and parse + JSON formatted strings back into the C representation of JSON objects. + It aims to conform to RFC 7159. +

+ +

Building

+

To setup JSON-C to build on your system please run configure and make.

+

If you are on Win32 cmake is required, generally:

+
    +
  • mkdir build
  • +
  • cd build
  • +
  • cmake ..
  • +
  • msbuild "json-c.vcxproj" /m /verbosity:normal /p:OutDir=lib\
  • +
  • Or, open the project in Visual Studio
  • +
+ +

Documentation

+

Doxygen generated documentation exists here.

+ +

GIT Reposository

+

git clone https://github.com/json-c/json-c.git

+ +

Mailing List

+ Send email to json-c <at> googlegroups <dot> com

+ +

License

+

This program is free software; you can redistribute it and/or modify it under the terms of the MIT License.

+
+ + diff --git a/comm/third_party/json-c/README.md b/comm/third_party/json-c/README.md new file mode 100644 index 0000000000..7e642a1111 --- /dev/null +++ b/comm/third_party/json-c/README.md @@ -0,0 +1,300 @@ +\mainpage + +`json-c` +======== + +1. [Overview and Build Status](#overview) +2. [Building on Unix](#buildunix) + * [Prerequisites](#installprereq) + * [Build commands](#buildcmds) +3. [CMake options](#CMake) +4. [Testing](#testing) +5. [Building with `vcpkg`](#buildvcpkg) +6. [Linking to libjson-c](#linking) +7. [Using json-c](#using) + +JSON-C - A JSON implementation in C +----------------------------------- + +JSON-C implements a reference counting object model that allows you to easily +construct JSON objects in C, output them as JSON formatted strings and parse +JSON formatted strings back into the C representation of JSON objects. +It aims to conform to [RFC 7159](https://tools.ietf.org/html/rfc7159). + +Skip down to [Using json-c](#using) +or check out the [API docs](https://json-c.github.io/json-c/), +if you already have json-c installed and ready to use. + +Home page for json-c: https://github.com/json-c/json-c/wiki + +Build Status +* [AppVeyor Build](https://ci.appveyor.com/project/hawicz/json-c) ![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/json-c/json-c?branch=master&svg=true) +* [Travis Build](https://travis-ci.org/json-c/json-c) ![Travis Build Status](https://travis-ci.org/json-c/json-c.svg?branch=master) + +Test Status +* [Coveralls](https://coveralls.io/github/json-c/json-c?branch=master) [![Coverage Status](https://coveralls.io/repos/github/json-c/json-c/badge.svg?branch=master)](https://coveralls.io/github/json-c/json-c?branch=master) + +Building on Unix with `git`, `gcc` and `cmake` +-------------------------------------------------- + +If you already have json-c installed, see [Linking to `libjson-c`](#linking) +for how to build and link your program against it. + +### Prerequisites: + + - `gcc`, `clang`, or another C compiler + + - `cmake>=2.8`, `>=3.16` recommended, `cmake=>3.1` for tests + +To generate docs you'll also need: + - `doxygen>=1.8.13` + +If you are on a relatively modern system, you'll likely be able to install +the prerequisites using your OS's packaging system. + +### Install using apt (e.g. Ubuntu 16.04.2 LTS) +```sh +sudo apt install git +sudo apt install cmake +sudo apt install doxygen # optional +sudo apt install valgrind # optional +``` + +### Build instructions: + +`json-c` GitHub repo: https://github.com/json-c/json-c + +```sh +$ git clone https://github.com/json-c/json-c.git +$ mkdir json-c-build +$ cd json-c-build +$ cmake ../json-c # See CMake section below for custom arguments +``` + +Note: it's also possible to put your build directory inside the json-c +source directory, or even not use a separate build directory at all, but +certain things might not work quite right (notably, `make distcheck`) + +Then: + +```sh +$ make +$ make test +$ make USE_VALGRIND=0 test # optionally skip using valgrind +$ make install +``` + + +### Generating documentation with Doxygen: + +The library documentation can be generated directly from the source code using Doxygen tool: + +```sh +# in build directory +make doc +google-chrome doc/html/index.html +``` + + +CMake Options +-------------------- + +The json-c library is built with [CMake](https://cmake.org/cmake-tutorial/), +which can take a few options. + +Variable | Type | Description +-----------------------------|--------|-------------- +CMAKE_INSTALL_PREFIX | String | The install location. +CMAKE_BUILD_TYPE | String | Defaults to "debug". +BUILD_SHARED_LIBS | Bool | The default build generates a dynamic (dll/so) library. Set this to OFF to create a static library only. +BUILD_STATIC_LIBS | Bool | The default build generates a static (lib/a) library. Set this to OFF to create a shared library only. +DISABLE_STATIC_FPIC | Bool | The default builds position independent code. Set this to OFF to create a shared library only. +DISABLE_BSYMBOLIC | Bool | Disable use of -Bsymbolic-functions. +DISABLE_THREAD_LOCAL_STORAGE | Bool | Disable use of Thread-Local Storage (HAVE___THREAD). +DISABLE_WERROR | Bool | Disable use of -Werror. +ENABLE_RDRAND | Bool | Enable RDRAND Hardware RNG Hash Seed. +ENABLE_THREADING | Bool | Enable partial threading support. +OVERRIDE_GET_RANDOM_SEED | String | A block of code to use instead of the default implementation of json_c_get_random_seed(), e.g. on embedded platforms where not even the fallback to time() works. Must be a single line. + +Pass these options as `-D` on CMake's command-line. + +```sh +# build a static library only +cmake -DBUILD_SHARED_LIBS=OFF .. +``` + +### Building with partial threading support + +Although json-c does not support fully multi-threaded access to +object trees, it has some code to help make its use in threaded programs +a bit safer. Currently, this is limited to using atomic operations for +json_object_get() and json_object_put(). + +Since this may have a performance impact, of at least 3x slower +according to https://stackoverflow.com/a/11609063, it is disabled by +default. You may turn it on by adjusting your cmake command with: + -DENABLE_THREADING=ON + +Separately, the default hash function used for object field keys, +lh_char_hash, uses a compare-and-swap operation to ensure the random +seed is only generated once. Because this is a one-time operation, it +is always compiled in when the compare-and-swap operation is available. + + +### cmake-configure wrapper script + +For those familiar with the old autoconf/autogen.sh/configure method, +there is a `cmake-configure` wrapper script to ease the transition to cmake. + +```sh +mkdir build +cd build +../cmake-configure --prefix=/some/install/path +make +``` + +cmake-configure can take a few options. + +| options | Description| +| ---- | ---- | +| prefix=PREFIX | install architecture-independent files in PREFIX | +| enable-threading | Enable code to support partly multi-threaded use | +| enable-rdrand | Enable RDRAND Hardware RNG Hash Seed generation on supported x86/x64 platforms. | +| enable-shared | build shared libraries [default=yes] | +| enable-static | build static libraries [default=yes] | +| disable-Bsymbolic | Avoid linking with -Bsymbolic-function | +| disable-werror | Avoid treating compiler warnings as fatal errors | + + +Testing: +---------- + +By default, if valgrind is available running tests uses it. +That can slow the tests down considerably, so to disable it use: +```sh +export USE_VALGRIND=0 +``` + +To run tests a separate build directory is recommended: +```sh +mkdir build-test +cd build-test +# VALGRIND=1 causes -DVALGRIND=1 to be passed when compiling code +# which uses slightly slower, but valgrind-safe code. +VALGRIND=1 cmake .. +make + +make test +# By default, if valgrind is available running tests uses it. +make USE_VALGRIND=0 test # optionally skip using valgrind +``` + +If a test fails, check `Testing/Temporary/LastTest.log`, +`tests/testSubDir/${testname}/${testname}.vg.out`, and other similar files. +If there is insufficient output try: +```sh +VERBOSE=1 CTEST_OUTPUT_ON_FAILURE=1 make test +``` +or +```sh +JSONC_TEST_TRACE=1 make test +``` +and check the log files again. + + +Building on Unix and Windows with `vcpkg` +-------------------------------------------------- + +You can download and install JSON-C using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + vcpkg install json-c + +The JSON-C port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + + +Linking to `libjson-c` +---------------------- + +If your system has `pkgconfig`, +then you can just add this to your `makefile`: + +```make +CFLAGS += $(shell pkg-config --cflags json-c) +LDFLAGS += $(shell pkg-config --libs json-c) +``` + +Without `pkgconfig`, you might do something like this: + +```make +JSON_C_DIR=/path/to/json_c/install +CFLAGS += -I$(JSON_C_DIR)/include/json-c +# Or to use lines like: #include +#CFLAGS += -I$(JSON_C_DIR)/include +LDFLAGS+= -L$(JSON_C_DIR)/lib -ljson-c +``` + +If your project uses cmake: + +* Add to your CMakeLists.txt file: + +```cmake +find_package(json-c CONFIG) +target_link_libraries(${PROJECT_NAME} PRIVATE json-c::json-c) +``` + +* Then you might run in your project: + +```sh +cd build +cmake -DCMAKE_PREFIX_PATH=/path/to/json_c/install/lib64/cmake .. +``` + +Using json-c +------------ + +To use json-c you can either include json.h, or preferably, one of the +following more specific header files: + +* json_object.h - Core types and methods. +* json_tokener.h - Methods for parsing and serializing json-c object trees. +* json_pointer.h - JSON Pointer (RFC 6901) implementation for retrieving + objects from a json-c object tree. +* json_object_iterator.h - Methods for iterating over single json_object instances. (See also `json_object_object_foreach()` in json_object.h) +* json_visit.h - Methods for walking a tree of json-c objects. +* json_util.h - Miscellaneous utility functions. + +For a full list of headers see [files.html](https://json-c.github.io/json-c/json-c-current-release/doc/html/files.html) + +The primary type in json-c is json_object. It describes a reference counted +tree of json objects which are created by either parsing text with a +json_tokener (i.e. `json_tokener_parse_ex()`), or by creating +(with `json_object_new_object()`, `json_object_new_int()`, etc...) and adding +(with `json_object_object_add()`, `json_object_array_add()`, etc...) them +individually. +Typically, every object in the tree will have one reference, from its parent. +When you are done with the tree of objects, you call json_object_put() on just +the root object to free it, which recurses down through any child objects +calling json_object_put() on each one of those in turn. + +You can get a reference to a single child +(`json_object_object_get()` or `json_object_array_get_idx()`) +and use that object as long as its parent is valid. +If you need a child object to live longer than its parent, you can +increment the child's refcount (`json_object_get()`) to allow it to survive +the parent being freed or it being removed from its parent +(`json_object_object_del()` or `json_object_array_del_idx()`) + +When parsing text, the json_tokener object is independent from the json_object +that it returns. It can be allocated (`json_tokener_new()`) +used one or multiple times (`json_tokener_parse_ex()`, and +freed (`json_tokener_free()`) while the json_object objects live on. + +A json_object tree can be serialized back into a string with +`json_object_to_json_string_ext()`. The string that is returned +is only valid until the next "to_json_string" call on that same object. +Also, it is freed when the json_object is freed. + diff --git a/comm/third_party/json-c/RELEASE_CHECKLIST.txt b/comm/third_party/json-c/RELEASE_CHECKLIST.txt new file mode 100644 index 0000000000..e8d2943a8a --- /dev/null +++ b/comm/third_party/json-c/RELEASE_CHECKLIST.txt @@ -0,0 +1,179 @@ + +# Release checklist: + +## Pre-release tasks + +* Figure out whether a release is worthwhile to do. +* Analyze the previous release branch to see if anything should have been + applied to master. +* Collect changes and assemble tentative release notes. + * Identify previous release branch point + * Check commit logs between previous branch point and now for + notable changes worth mentioning + * Create a new issues_closed_for_X.Y.md file + * Include notable entries from here in the release notes. + * Analyze APIs between previous release branch and master to produce list of + changes (added/removed/updated funcs, etc...), and detect backwards compat + issues. + * https://github.com/lvc/abi-compliance-checker + * If the new release is not backwards compatible, then this is a MAJOR release. + * Mention removed features in ChangeLog + * Consider re-adding backwards compatible support, through symbol + aliases and appropriate entries in json-c.sym + * Be sure any new symbols are listed in json-c.sym as part of + the _new_ release version. + * Update the AUTHORS file + + PREV=$(git tag | tail -1) + ( git log -r ${PREV}..HEAD | grep Author: | sed -e's/Author: //' ; cat AUTHORS ) | sort -u > A1 + mv A1 AUTHORS + + * Exclude mentioning changes that have already been included in a point + release of the previous release branch. + +* Update ChangeLog with relevant notes before branching. + +* Check that the compile works on Linux - automatic through Travis +* Check that the compile works on NetBSD +* Check that the compile works on Windows - automatic through AppVeyor + +## Release creation + +Start creating the new release: + release=0.16 + git clone https://github.com/json-c/json-c json-c-${release} + + mkdir distcheck + cd distcheck + # Note, the build directory *must* be entirely separate from + # the source tree for distcheck to work properly. + cmake -DCMAKE_BUILD_TYPE=Release ../json-c-${release} + make distcheck + cd .. + +Make any fixes/changes *before* branching. + + cd json-c-${release} + git branch json-c-${release} + git checkout json-c-${release} + +------------ + +Using ${release}: + Update the version in json_c_version.h + Update the version in CMakeLists.txt (VERSION in the project(...) line) + +Update the set_target_properties() line in CmakeLists.txt to set the shared +library version. Generally, unless we're doing a major release, change: + VERSION x.y.z +to + VERSION x.y+1.z + + git commit -a -m "Bump version to ${release}" + +If we're doing a major release (SONAME bump), also bump the version + of ALL symbols in json-c.sym. + See explanation at https://github.com/json-c/json-c/issues/621 + More info at: https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf + +------------ + +Generate the doxygen documentation: + + (cd ../distcheck && make doc) + cp -r -p ../distcheck/doc/{html,Doxyfile} doc/. + rm doc/Doxyfile # Remove generated file w/ hardcoded paths + git add -f doc + git commit doc -m "Generate docs for the ${release} release" + +------------ + +Create the release tarballs: + + cd .. + echo .git > excludes + tar -czf json-c-${release}.tar.gz -X excludes json-c-${release} + + echo 'doc/*' >> excludes + tar -czf json-c-${release}-nodoc.tar.gz -X excludes json-c-${release} + +------------ + +Tag the branch: + + cd json-c-${release} + git tag -a json-c-${release}-$(date +%Y%m%d) -m "Release json-c-${release}" + + git push origin json-c-${release} + git push --tags + +------------ + +Go to Amazon S3 service at: + https://console.aws.amazon.com/s3/ + +Upload the two tarballs in the json-c_releases/releases folder. + When uploading, use "Standard" storage class, and make the uploaded files publicly accessible. + +Logout of Amazon S3, and verify that the files are visible. + https://s3.amazonaws.com/json-c_releases/releases/index.html + +=================================== + +Post-release checklist: + + git checkout master + +Add new section to ChangeLog for ${release}+1 + +Use ${release}.99 to indicate a version "newer" than anything on the branch: + Update the version in json_c_version.h + Update the version in CMakeLists.txt + +Update RELEASE_CHECKLIST.txt, set release=${release}+1 + +Add a new empty section to the json-c.sym file, for ${release}+1 + +Update the set_target_properties() line in CmakeLists.txt to match the release branch. + + git commit -a -m "Update the master branch to version ${release}.99" + git push + +------------ + +Update the gh-pages branch with new docs: + + cd json-c-${release} + git checkout json-c-${release} + cd .. + + git clone -b gh-pages https://github.com/json-c/json-c json-c-pages + cd json-c-pages + mkdir json-c-${release} + cp -R ../json-c-${release}/doc json-c-${release}/. + git add json-c-${release} + rm json-c-current-release + ln -s json-c-${release} json-c-current-release + git commit -a -m "Add the ${release} docs." + + vi index.html + # Add/change links to current release. + + git commit -a -m "Update the doc links to point at ${release}" + + git push + +------------ + +Update checksums on wiki page. + + cd .. + openssl sha -sha256 json-c*gz + openssl md5 json-c*gz + +Copy and paste this output into the wiki page at: + https://github.com/json-c/json-c/wiki + +------------ + +Send an email to the mailing list. diff --git a/comm/third_party/json-c/STYLE.txt b/comm/third_party/json-c/STYLE.txt new file mode 100644 index 0000000000..4e5d75ac5b --- /dev/null +++ b/comm/third_party/json-c/STYLE.txt @@ -0,0 +1,31 @@ +In general: +For minor changes to a function, copy the existing formatting. +When changing the style, commit that separately from other changes. +For new code and major changes to a function, switch to the official json-c style. + +Official json-c style: + +Aim for readability, not strict conformance to fixed style rules. +Formatting is tab based; previous attempts at proper alignment with +spaces for continuation lines have been abandoned in favor of the +convenience of using clang-format. +Refer to the .clang-format file for details, and run the tool before commit: + + clang-format -i somefile.c foo.h + +For sections of code that would be significantly negatively impacted, surround +them with magic comments to disable formatting: + + /* clang-format off */ + ...code... + /* clang-format on */ + + +Naming: +Words within function and variable names are separated with underscores. Avoid camel case. +Prefer longer, more descriptive names, but not excessively so. No single letter variable names. + +Other: +Variables should be defined for the smallest scope needed. +Functions should be defined static when possible. +When possible, avoid exposing internals in the public API. diff --git a/comm/third_party/json-c/arraylist.c b/comm/third_party/json-c/arraylist.c new file mode 100644 index 0000000000..d8e12d11cb --- /dev/null +++ b/comm/third_party/json-c/arraylist.c @@ -0,0 +1,205 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include + +#ifdef STDC_HEADERS +#include +#include +#endif /* STDC_HEADERS */ + +#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) +#include +#endif /* HAVE_STRINGS_H */ + +#ifndef SIZE_T_MAX +#if SIZEOF_SIZE_T == SIZEOF_INT +#define SIZE_T_MAX UINT_MAX +#elif SIZEOF_SIZE_T == SIZEOF_LONG +#define SIZE_T_MAX ULONG_MAX +#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG +#define SIZE_T_MAX ULLONG_MAX +#else +#error Unable to determine size of size_t +#endif +#endif + +#include "arraylist.h" + +struct array_list *array_list_new(array_list_free_fn *free_fn) +{ + return array_list_new2(free_fn, ARRAY_LIST_DEFAULT_SIZE); +} + +struct array_list *array_list_new2(array_list_free_fn *free_fn, int initial_size) +{ + struct array_list *arr; + + if (initial_size < 0 || (size_t)initial_size >= SIZE_T_MAX / sizeof(void *)) + return NULL; + arr = (struct array_list *)malloc(sizeof(struct array_list)); + if (!arr) + return NULL; + arr->size = initial_size; + arr->length = 0; + arr->free_fn = free_fn; + if (!(arr->array = (void **)malloc(arr->size * sizeof(void *)))) + { + free(arr); + return NULL; + } + return arr; +} + +extern void array_list_free(struct array_list *arr) +{ + size_t i; + for (i = 0; i < arr->length; i++) + if (arr->array[i]) + arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void *array_list_get_idx(struct array_list *arr, size_t i) +{ + if (i >= arr->length) + return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, size_t max) +{ + void *t; + size_t new_size; + + if (max < arr->size) + return 0; + /* Avoid undefined behaviour on size_t overflow */ + if (arr->size >= SIZE_T_MAX / 2) + new_size = max; + else + { + new_size = arr->size << 1; + if (new_size < max) + new_size = max; + } + if (new_size > (~((size_t)0)) / sizeof(void *)) + return -1; + if (!(t = realloc(arr->array, new_size * sizeof(void *)))) + return -1; + arr->array = (void **)t; + arr->size = new_size; + return 0; +} + +int array_list_shrink(struct array_list *arr, size_t empty_slots) +{ + void *t; + size_t new_size; + + if (empty_slots >= SIZE_T_MAX / sizeof(void *) - arr->length) + return -1; + new_size = arr->length + empty_slots; + if (new_size == arr->size) + return 0; + if (new_size > arr->size) + return array_list_expand_internal(arr, new_size); + if (new_size == 0) + new_size = 1; + + if (!(t = realloc(arr->array, new_size * sizeof(void *)))) + return -1; + arr->array = (void **)t; + arr->size = new_size; + return 0; +} + +//static inline int _array_list_put_idx(struct array_list *arr, size_t idx, void *data) +int array_list_put_idx(struct array_list *arr, size_t idx, void *data) +{ + if (idx > SIZE_T_MAX - 1) + return -1; + if (array_list_expand_internal(arr, idx + 1)) + return -1; + if (idx < arr->length && arr->array[idx]) + arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if (idx > arr->length) + { + /* Zero out the arraylist slots in between the old length + and the newly added entry so we know those entries are + empty. + e.g. when setting array[7] in an array that used to be + only 5 elements longs, array[5] and array[6] need to be + set to 0. + */ + memset(arr->array + arr->length, 0, (idx - arr->length) * sizeof(void *)); + } + if (arr->length <= idx) + arr->length = idx + 1; + return 0; +} + +int array_list_add(struct array_list *arr, void *data) +{ + /* Repeat some of array_list_put_idx() so we can skip several + checks that we know are unnecessary when appending at the end + */ + size_t idx = arr->length; + if (idx > SIZE_T_MAX - 1) + return -1; + if (array_list_expand_internal(arr, idx + 1)) + return -1; + arr->array[idx] = data; + arr->length++; + return 0; +} + +void array_list_sort(struct array_list *arr, int (*compar)(const void *, const void *)) +{ + qsort(arr->array, arr->length, sizeof(arr->array[0]), compar); +} + +void *array_list_bsearch(const void **key, struct array_list *arr, + int (*compar)(const void *, const void *)) +{ + return bsearch(key, arr->array, arr->length, sizeof(arr->array[0]), compar); +} + +size_t array_list_length(struct array_list *arr) +{ + return arr->length; +} + +int array_list_del_idx(struct array_list *arr, size_t idx, size_t count) +{ + size_t i, stop; + + /* Avoid overflow in calculation with large indices. */ + if (idx > SIZE_T_MAX - count) + return -1; + stop = idx + count; + if (idx >= arr->length || stop > arr->length) + return -1; + for (i = idx; i < stop; ++i) + { + // Because put_idx can skip entries, we need to check if + // there's actually anything in each slot we're erasing. + if (arr->array[i]) + arr->free_fn(arr->array[i]); + } + memmove(arr->array + idx, arr->array + stop, (arr->length - stop) * sizeof(void *)); + arr->length -= count; + return 0; +} diff --git a/comm/third_party/json-c/arraylist.h b/comm/third_party/json-c/arraylist.h new file mode 100644 index 0000000000..f541706936 --- /dev/null +++ b/comm/third_party/json-c/arraylist.h @@ -0,0 +1,88 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Internal methods for working with json_type_array objects. + * Although this is exposed by the json_object_get_array() method, + * it is not recommended for direct use. + */ +#ifndef _json_c_arraylist_h_ +#define _json_c_arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void(array_list_free_fn)(void *data); + +struct array_list +{ + void **array; + size_t length; + size_t size; + array_list_free_fn *free_fn; +}; +typedef struct array_list array_list; + +/** + * Allocate an array_list of the default size (32). + * @deprecated Use array_list_new2() instead. + */ +extern struct array_list *array_list_new(array_list_free_fn *free_fn); + +/** + * Allocate an array_list of the desired size. + * + * If possible, the size should be chosen to closely match + * the actual number of elements expected to be used. + * If the exact size is unknown, there are tradeoffs to be made: + * - too small - the array_list code will need to call realloc() more + * often (which might incur an additional memory copy). + * - too large - will waste memory, but that can be mitigated + * by calling array_list_shrink() once the final size is known. + * + * @see array_list_shrink + */ +extern struct array_list *array_list_new2(array_list_free_fn *free_fn, int initial_size); + +extern void array_list_free(struct array_list *al); + +extern void *array_list_get_idx(struct array_list *al, size_t i); + +extern int array_list_put_idx(struct array_list *al, size_t i, void *data); + +extern int array_list_add(struct array_list *al, void *data); + +extern size_t array_list_length(struct array_list *al); + +extern void array_list_sort(struct array_list *arr, int (*compar)(const void *, const void *)); + +extern void *array_list_bsearch(const void **key, struct array_list *arr, + int (*compar)(const void *, const void *)); + +extern int array_list_del_idx(struct array_list *arr, size_t idx, size_t count); + +/** + * Shrink the array list to just enough to fit the number of elements in it, + * plus empty_slots. + */ +extern int array_list_shrink(struct array_list *arr, size_t empty_slots); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/config.h.in b/comm/third_party/json-c/config.h.in new file mode 100644 index 0000000000..06c0f20886 --- /dev/null +++ b/comm/third_party/json-c/config.h.in @@ -0,0 +1,217 @@ +/* config.h.in. Modified for moz.build from cmake/config.h.in. */ + +/* Enable RDRAND Hardware RNG Hash Seed */ +#undef ENABLE_RDRAND + +/* Override json_c_get_random_seed() with custom code */ +#undef OVERRIDE_GET_RANDOM_SEED + +/* Enable partial threading support */ +#undef ENABLE_THREADING + +/* Define if .gnu.warning accepts long strings. */ +#undef HAS_GNU_WARNING_LONG + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_CDEFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RANDOM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_XLOCALE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_BSD_STDLIB_H + +/* Define to 1 if you have `arc4random' */ +#undef HAVE_ARC4RANDOM + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Has atomic builtins */ +#undef HAVE_ATOMIC_BUILTINS + +/* Define to 1 if you have the declaration of `INFINITY', and to 0 if you + don't. */ +#undef HAVE_DECL_INFINITY + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. + */ +#undef HAVE_DECL_ISINF + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. + */ +#undef HAVE_DECL_ISNAN + +/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ +#undef HAVE_DECL_NAN + +/* Define to 1 if you have the declaration of `_finite', and to 0 if you + don't. */ +#undef HAVE_DECL__FINITE + +/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. + */ +#undef HAVE_DECL__ISNAN + +/* Define to 1 if you have the `open' function. */ +#undef HAVE_OPEN + +/* Define to 1 if you have the `realloc' function. */ +#undef HAVE_REALLOC + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `uselocale' function. */ +#undef HAVE_USELOCALE + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Define if you have the `getrandom' function. */ +#undef HAVE_GETRANDOM + +/* Define if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + +/* Have __thread */ +#undef HAVE___THREAD + +/* Public define for json_inttypes.h */ +#undef JSON_C_HAVE_INTTYPES_H + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The number of bytes in type int */ +#undef SIZEOF_INT + +/* The number of bytes in type int64_t */ +#undef SIZEOF_INT64_T + +/* The number of bytes in type long */ +#undef SIZEOF_LONG + +/* The number of bytes in type long long */ +#undef SIZEOF_LONG_LONG + +/* The number of bytes in type size_t */ +#undef SIZEOF_SIZE_T + +/* The number of bytes in type ssize_t */ +#undef SIZEOF_SSIZE_T + +/* Specifier for __thread */ +#undef SPEC___THREAD + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/comm/third_party/json-c/debug.c b/comm/third_party/json-c/debug.c new file mode 100644 index 0000000000..7971744ccf --- /dev/null +++ b/comm/third_party/json-c/debug.c @@ -0,0 +1,96 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +#include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) +{ + _debug = debug; +} +int mc_get_debug(void) +{ + return _debug; +} + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if (_debug) + { + va_start(ap, msg); +#if HAVE_VSYSLOG + if (_syslog) + { + vsyslog(LOG_DEBUG, msg, ap); + } + else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if (_syslog) + { + vsyslog(LOG_ERR, msg, ap); + } + else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if (_syslog) + { + vsyslog(LOG_INFO, msg, ap); + } + else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} diff --git a/comm/third_party/json-c/debug.h b/comm/third_party/json-c/debug.h new file mode 100644 index 0000000000..4af0ba9236 --- /dev/null +++ b/comm/third_party/json-c/debug.h @@ -0,0 +1,98 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _JSON_C_DEBUG_H_ +#define _JSON_C_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JSON_EXPORT +#if defined(_MSC_VER) && defined(JSON_C_DLL) +#define JSON_EXPORT __declspec(dllexport) +#else +#define JSON_EXPORT extern +#endif +#endif + +JSON_EXPORT void mc_set_debug(int debug); +JSON_EXPORT int mc_get_debug(void); + +JSON_EXPORT void mc_set_syslog(int syslog); + +JSON_EXPORT void mc_debug(const char *msg, ...); +JSON_EXPORT void mc_error(const char *msg, ...); +JSON_EXPORT void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) \ + do \ + { \ + } while (0) + +#else + +#define JASSERT(cond) \ + do \ + { \ + if (!(cond)) \ + { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", \ + __FILE__, __LINE__); \ + *(int *)0 = 1; \ + abort(); \ + } \ + } while (0) + +#endif + +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) \ + if (0) \ + mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) \ + if (0) \ + mc_set_syslog(x) +#define MC_DEBUG(x, ...) \ + if (0) \ + mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) \ + if (0) \ + mc_info(x, ##__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json.h.cmakein b/comm/third_party/json-c/json.h.cmakein new file mode 100644 index 0000000000..4fed013b31 --- /dev/null +++ b/comm/third_party/json-c/json.h.cmakein @@ -0,0 +1,38 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief A convenience header that may be included instead of other individual ones. + */ +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "arraylist.h" +#include "debug.h" +#include "json_c_version.h" +#include "json_object.h" +#include "json_object_iterator.h" +@JSON_H_JSON_POINTER@ +#include "json_tokener.h" +#include "json_util.h" +#include "linkhash.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_c_version.c b/comm/third_party/json-c/json_c_version.c new file mode 100644 index 0000000000..9b21db4546 --- /dev/null +++ b/comm/third_party/json-c/json_c_version.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} diff --git a/comm/third_party/json-c/json_c_version.h b/comm/third_party/json-c/json_c_version.h new file mode 100644 index 0000000000..1b7d531a69 --- /dev/null +++ b/comm/third_party/json-c/json_c_version.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012,2017-2022 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +/** + * @file + * @brief Methods for retrieving the json-c version. + */ +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 16 +#define JSON_C_MICRO_VERSION 0 +#define JSON_C_VERSION_NUM \ + ((JSON_C_MAJOR_VERSION << 16) | (JSON_C_MINOR_VERSION << 8) | JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.16" + +#ifndef JSON_EXPORT +#if defined(_MSC_VER) && defined(JSON_C_DLL) +#define JSON_EXPORT __declspec(dllexport) +#else +#define JSON_EXPORT extern +#endif +#endif + +/** + * @see JSON_C_VERSION + * @return the version of the json-c library as a string + */ +JSON_EXPORT const char *json_c_version(void); /* Returns JSON_C_VERSION */ + +/** + * The json-c version encoded into an int, with the low order 8 bits + * being the micro version, the next higher 8 bits being the minor version + * and the next higher 8 bits being the major version. + * For example, 7.12.99 would be 0x00070B63. + * + * @see JSON_C_VERSION_NUM + * @return the version of the json-c library as an int + */ +JSON_EXPORT int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_config.h.in b/comm/third_party/json-c/json_config.h.in new file mode 100644 index 0000000000..7888e02170 --- /dev/null +++ b/comm/third_party/json-c/json_config.h.in @@ -0,0 +1,3 @@ + +/* Define to 1 if you have the header file. */ +#undef JSON_C_HAVE_INTTYPES_H diff --git a/comm/third_party/json-c/json_inttypes.h b/comm/third_party/json-c/json_inttypes.h new file mode 100644 index 0000000000..e047d4f18b --- /dev/null +++ b/comm/third_party/json-c/json_inttypes.h @@ -0,0 +1,24 @@ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#ifdef JSON_C_HAVE_INTTYPES_H +/* inttypes.h includes stdint.h */ +#include + +#else +#include + +#define PRId64 "I64d" +#define SCNd64 "I64d" +#define PRIu64 "I64u" + +#endif + +#endif diff --git a/comm/third_party/json-c/json_object.c b/comm/third_party/json-c/json_object.c new file mode 100644 index 0000000000..e52ca4071a --- /dev/null +++ b/comm/third_party/json-c/json_object.c @@ -0,0 +1,1808 @@ +/* + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include "strerror_override.h" + +#include +#ifdef HAVE_LIMITS_H +#include +#endif +#include +#include +#include +#include +#include + +#include "arraylist.h" +#include "debug.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" +#include "linkhash.h" +#include "math_compat.h" +#include "printbuf.h" +#include "snprintf_compat.h" +#include "strdup_compat.h" + +/* Avoid ctype.h and locale overhead */ +#define is_plain_digit(c) ((c) >= '0' && (c) <= '9') + +#if SIZEOF_LONG_LONG != SIZEOF_INT64_T +#error The long long type is not 64-bits +#endif + +#ifndef SSIZE_T_MAX +#if SIZEOF_SSIZE_T == SIZEOF_INT +#define SSIZE_T_MAX INT_MAX +#elif SIZEOF_SSIZE_T == SIZEOF_LONG +#define SSIZE_T_MAX LONG_MAX +#elif SIZEOF_SSIZE_T == SIZEOF_LONG_LONG +#define SSIZE_T_MAX LLONG_MAX +#else +#error Unable to determine size of ssize_t +#endif +#endif + +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object *jso); + +#if defined(_MSC_VER) && (_MSC_VER <= 1800) +/* VS2013 doesn't know about "inline" */ +#define inline __inline +#elif defined(AIX_CC) +#define inline +#endif + +/* + * Helper functions to more safely cast to a particular type of json_object + */ +static inline struct json_object_object *JC_OBJECT(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_object *JC_OBJECT_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_array *JC_ARRAY(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_array *JC_ARRAY_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_boolean *JC_BOOL(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_boolean *JC_BOOL_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_double *JC_DOUBLE(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_double *JC_DOUBLE_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_int *JC_INT(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_int *JC_INT_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_string *JC_STRING(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_string *JC_STRING_C(const struct json_object *jso) +{ + return (const void *)jso; +} + +#define JC_CONCAT(a, b) a##b +#define JC_CONCAT3(a, b, c) a##b##c + +#define JSON_OBJECT_NEW(jtype) \ + (struct JC_CONCAT(json_object_, jtype) *)json_object_new( \ + JC_CONCAT(json_type_, jtype), sizeof(struct JC_CONCAT(json_object_, jtype)), \ + &JC_CONCAT3(json_object_, jtype, _to_json_string)) + +static inline struct json_object *json_object_new(enum json_type o_type, size_t alloc_size, + json_object_to_json_string_fn *to_json_string); + +static void json_object_object_delete(struct json_object *jso_base); +static void json_object_string_delete(struct json_object *jso); +static void json_object_array_delete(struct json_object *jso); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string_default; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; +static json_object_to_json_string_fn _json_object_userdata_to_json_string; + +#ifndef JSON_NORETURN +#if defined(_MSC_VER) +#define JSON_NORETURN __declspec(noreturn) +#elif defined(__OS400__) +#define JSON_NORETURN +#else +/* 'cold' attribute is for optimization, telling the computer this code + * path is unlikely. + */ +#define JSON_NORETURN __attribute__((noreturn, cold)) +#endif +#endif +/** + * Abort and optionally print a message on standard error. + * This should be used rather than assert() for unconditional abortion + * (in particular for code paths which are never supposed to be run). + * */ +JSON_NORETURN static void json_abort(const char *message); + +/* helper for accessing the optimized string data component in json_object + */ +static inline char *get_string_component_mutable(struct json_object *jso) +{ + if (JC_STRING_C(jso)->len < 0) + { + /* Due to json_object_set_string(), we might have a pointer */ + return JC_STRING(jso)->c_string.pdata; + } + return JC_STRING(jso)->c_string.idata; +} +static inline const char *get_string_component(const struct json_object *jso) +{ + return get_string_component_mutable((void *)(uintptr_t)(const void *)jso); +} + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int flags) +{ + size_t pos = 0, start_offset = 0; + unsigned char c; + while (len) + { + --len; + c = str[pos]; + switch (c) + { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if ((flags & JSON_C_TO_STRING_NOSLASHESCAPE) && c == '/') + { + pos++; + break; + } + + if (pos > start_offset) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + + if (c == '\b') + printbuf_memappend(pb, "\\b", 2); + else if (c == '\n') + printbuf_memappend(pb, "\\n", 2); + else if (c == '\r') + printbuf_memappend(pb, "\\r", 2); + else if (c == '\t') + printbuf_memappend(pb, "\\t", 2); + else if (c == '\f') + printbuf_memappend(pb, "\\f", 2); + else if (c == '"') + printbuf_memappend(pb, "\\\"", 2); + else if (c == '\\') + printbuf_memappend(pb, "\\\\", 2); + else if (c == '/') + printbuf_memappend(pb, "\\/", 2); + + start_offset = ++pos; + break; + default: + if (c < ' ') + { + char sbuf[7]; + if (pos > start_offset) + printbuf_memappend(pb, str + start_offset, + pos - start_offset); + snprintf(sbuf, sizeof(sbuf), "\\u00%c%c", json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + printbuf_memappend_fast(pb, sbuf, (int)sizeof(sbuf) - 1); + start_offset = ++pos; + } + else + pos++; + } + } + if (pos > start_offset) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + +/* reference counting */ + +struct json_object *json_object_get(struct json_object *jso) +{ + if (!jso) + return jso; + + // Don't overflow the refcounter. + assert(jso->_ref_count < UINT32_MAX); + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + __sync_add_and_fetch(&jso->_ref_count, 1); +#else + ++jso->_ref_count; +#endif + + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if (!jso) + return 0; + + /* Avoid invalid free and crash explicitly instead of (silently) + * segfaulting. + */ + assert(jso->_ref_count > 0); + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + /* Note: this only allow the refcount to remain correct + * when multiple threads are adjusting it. It is still an error + * for a thread to decrement the refcount if it doesn't "own" it, + * as that can result in the thread that loses the race to 0 + * operating on an already-freed object. + */ + if (__sync_sub_and_fetch(&jso->_ref_count, 1) > 0) + return 0; +#else + if (--jso->_ref_count > 0) + return 0; +#endif + + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + switch (jso->o_type) + { + case json_type_object: json_object_object_delete(jso); break; + case json_type_array: json_object_array_delete(jso); break; + case json_type_string: json_object_string_delete(jso); break; + default: json_object_generic_delete(jso); break; + } + return 1; +} + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object *jso) +{ + printbuf_free(jso->_pb); + free(jso); +} + +static inline struct json_object *json_object_new(enum json_type o_type, size_t alloc_size, + json_object_to_json_string_fn *to_json_string) +{ + struct json_object *jso; + + jso = (struct json_object *)malloc(alloc_size); + if (!jso) + return NULL; + + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_to_json_string = to_json_string; + jso->_pb = NULL; + jso->_user_delete = NULL; + jso->_userdata = NULL; + //jso->... // Type-specific fields must be set by caller + + return jso; +} + +/* type checking functions */ + +int json_object_is_type(const struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(const struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +void *json_object_get_userdata(json_object *jso) +{ + return jso ? jso->_userdata : NULL; +} + +void json_object_set_userdata(json_object *jso, void *userdata, json_object_delete_fn *user_delete) +{ + // Can't return failure, so abort if we can't perform the operation. + assert(jso != NULL); + + // First, clean up any previously existing user info + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, json_object_to_json_string_fn *to_string_func, + void *userdata, json_object_delete_fn *user_delete) +{ + json_object_set_userdata(jso, userdata, user_delete); + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch (jso->o_type) + { + case json_type_null: jso->_to_json_string = NULL; break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string_default; + break; + case json_type_int: jso->_to_json_string = &json_object_int_to_json_string; break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; +} + +/* extended conversion to string */ + +const char *json_object_to_json_string_length(struct json_object *jso, int flags, size_t *length) +{ + const char *r = NULL; + size_t s = 0; + + if (!jso) + { + s = 4; + r = "null"; + } + else if ((jso->_pb) || (jso->_pb = printbuf_new())) + { + printbuf_reset(jso->_pb); + + if (jso->_to_json_string(jso, jso->_pb, 0, flags) >= 0) + { + s = (size_t)jso->_pb->bpos; + r = jso->_pb->buf; + } + } + + if (length) + *length = s; + return r; +} + +const char *json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + return json_object_to_json_string_length(jso, flags, NULL); +} + +/* backwards-compatible conversion to string */ + +const char *json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (flags & JSON_C_TO_STRING_PRETTY_TAB) + { + printbuf_memset(pb, -1, '\t', level); + } + else + { + printbuf_memset(pb, -1, ' ', level * 2); + } + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + printbuf_strappend(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + printbuf_strappend(pb, " "); + indent(pb, level + 1, flags); + printbuf_strappend(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key), flags); + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, "\": "); + else + printbuf_strappend(pb, "\":"); + if (iter.val == NULL) + printbuf_strappend(pb, "null"); + else if (iter.val->_to_json_string(iter.val, pb, level + 1, flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb, level, flags); + } + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + return printbuf_strappend(pb, /*{*/ " }"); + else + return printbuf_strappend(pb, /*{*/ "}"); +} + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + if (!lh_entry_k_is_constant(ent)) + free(lh_entry_k(ent)); + json_object_put((struct json_object *)lh_entry_v(ent)); +} + +static void json_object_object_delete(struct json_object *jso_base) +{ + lh_table_free(JC_OBJECT(jso_base)->c_object); + json_object_generic_delete(jso_base); +} + +struct json_object *json_object_new_object(void) +{ + struct json_object_object *jso = JSON_OBJECT_NEW(object); + if (!jso) + return NULL; + jso->c_object = + lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, &json_object_lh_entry_free); + if (!jso->c_object) + { + json_object_generic_delete(&jso->base); + errno = ENOMEM; + return NULL; + } + return &jso->base; +} + +struct lh_table *json_object_get_object(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch (jso->o_type) + { + case json_type_object: return JC_OBJECT_C(jso)->c_object; + default: return NULL; + } +} + +int json_object_object_add_ex(struct json_object *jso, const char *const key, + struct json_object *const val, const unsigned opts) +{ + struct json_object *existing_value = NULL; + struct lh_entry *existing_entry; + unsigned long hash; + + assert(json_object_get_type(jso) == json_type_object); + + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + hash = lh_get_hash(JC_OBJECT(jso)->c_object, (const void *)key); + existing_entry = + (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) + ? NULL + : lh_table_lookup_entry_w_hash(JC_OBJECT(jso)->c_object, (const void *)key, hash); + + // The caller must avoid creating loops in the object tree, but do a + // quick check anyway to make sure we're not creating a trivial loop. + if (jso == val) + return -1; + + if (!existing_entry) + { + const void *const k = + (opts & JSON_C_OBJECT_ADD_CONSTANT_KEY) ? (const void *)key : strdup(key); + if (k == NULL) + return -1; + return lh_table_insert_w_hash(JC_OBJECT(jso)->c_object, k, val, hash, opts); + } + existing_value = (json_object *)lh_entry_v(existing_entry); + if (existing_value) + json_object_put(existing_value); + lh_entry_set_val(existing_entry, val); + return 0; +} + +int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) +{ + return json_object_object_add_ex(jso, key, val, 0); +} + +int json_object_object_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_object); + return lh_table_length(JC_OBJECT_C(jso)->c_object); +} + +size_t json_c_object_sizeof(void) +{ + return sizeof(struct json_object); +} + +struct json_object *json_object_object_get(const struct json_object *jso, const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, + struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return 0; + + switch (jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(JC_OBJECT_C(jso)->c_object, (const void *)key, + (void **)value); + default: + if (value != NULL) + *value = NULL; + return 0; + } +} + +void json_object_object_del(struct json_object *jso, const char *key) +{ + assert(json_object_get_type(jso) == json_type_object); + lh_table_delete(JC_OBJECT(jso)->c_object, key); +} + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + if (JC_BOOL(jso)->c_boolean) + return printbuf_strappend(pb, "true"); + return printbuf_strappend(pb, "false"); +} + +struct json_object *json_object_new_boolean(json_bool b) +{ + struct json_object_boolean *jso = JSON_OBJECT_NEW(boolean); + if (!jso) + return NULL; + jso->c_boolean = b; + return &jso->base; +} + +json_bool json_object_get_boolean(const struct json_object *jso) +{ + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_int: + switch (JC_INT_C(jso)->cint_type) + { + case json_object_int_type_int64: return (JC_INT_C(jso)->cint.c_int64 != 0); + case json_object_int_type_uint64: return (JC_INT_C(jso)->cint.c_uint64 != 0); + default: json_abort("invalid cint_type"); + } + case json_type_double: return (JC_DOUBLE_C(jso)->c_double != 0); + case json_type_string: return (JC_STRING_C(jso)->len != 0); + default: return 0; + } +} + +int json_object_set_boolean(struct json_object *jso, json_bool new_value) +{ + if (!jso || jso->o_type != json_type_boolean) + return 0; + JC_BOOL(jso)->c_boolean = new_value; + return 1; +} + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + /* room for 19 digits, the sign char, and a null term */ + char sbuf[21]; + if (JC_INT(jso)->cint_type == json_object_int_type_int64) + snprintf(sbuf, sizeof(sbuf), "%" PRId64, JC_INT(jso)->cint.c_int64); + else + snprintf(sbuf, sizeof(sbuf), "%" PRIu64, JC_INT(jso)->cint.c_uint64); + return printbuf_memappend(pb, sbuf, strlen(sbuf)); +} + +struct json_object *json_object_new_int(int32_t i) +{ + return json_object_new_int64(i); +} + +int32_t json_object_get_int(const struct json_object *jso) +{ + int64_t cint64 = 0; + double cdouble; + enum json_type o_type; + + if (!jso) + return 0; + + o_type = jso->o_type; + if (o_type == json_type_int) + { + const struct json_object_int *jsoint = JC_INT_C(jso); + if (jsoint->cint_type == json_object_int_type_int64) + { + cint64 = jsoint->cint.c_int64; + } + else + { + if (jsoint->cint.c_uint64 >= INT64_MAX) + cint64 = INT64_MAX; + else + cint64 = (int64_t)jsoint->cint.c_uint64; + } + } + else if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(get_string_component(jso), &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch (o_type) + { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + if (cint64 >= INT32_MAX) + return INT32_MAX; + return (int32_t)cint64; + case json_type_double: + cdouble = JC_DOUBLE_C(jso)->c_double; + if (cdouble <= INT32_MIN) + return INT32_MIN; + if (cdouble >= INT32_MAX) + return INT32_MAX; + return (int32_t)cdouble; + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + default: return 0; + } +} + +int json_object_set_int(struct json_object *jso, int new_value) +{ + return json_object_set_int64(jso, (int64_t)new_value); +} + +struct json_object *json_object_new_int64(int64_t i) +{ + struct json_object_int *jso = JSON_OBJECT_NEW(int); + if (!jso) + return NULL; + jso->cint.c_int64 = i; + jso->cint_type = json_object_int_type_int64; + return &jso->base; +} + +struct json_object *json_object_new_uint64(uint64_t i) +{ + struct json_object_int *jso = JSON_OBJECT_NEW(int); + if (!jso) + return NULL; + jso->cint.c_uint64 = i; + jso->cint_type = json_object_int_type_uint64; + return &jso->base; +} + +int64_t json_object_get_int64(const struct json_object *jso) +{ + int64_t cint; + + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_int: + { + const struct json_object_int *jsoint = JC_INT_C(jso); + switch (jsoint->cint_type) + { + case json_object_int_type_int64: return jsoint->cint.c_int64; + case json_object_int_type_uint64: + if (jsoint->cint.c_uint64 >= INT64_MAX) + return INT64_MAX; + return (int64_t)jsoint->cint.c_uint64; + default: json_abort("invalid cint_type"); + } + } + case json_type_double: + // INT64_MAX can't be exactly represented as a double + // so cast to tell the compiler it's ok to round up. + if (JC_DOUBLE_C(jso)->c_double >= (double)INT64_MAX) + return INT64_MAX; + if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN) + return INT64_MIN; + return (int64_t)JC_DOUBLE_C(jso)->c_double; + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_string: + if (json_parse_int64(get_string_component(jso), &cint) == 0) + return cint; + /* FALLTHRU */ + default: return 0; + } +} + +uint64_t json_object_get_uint64(const struct json_object *jso) +{ + uint64_t cuint; + + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_int: + { + const struct json_object_int *jsoint = JC_INT_C(jso); + switch (jsoint->cint_type) + { + case json_object_int_type_int64: + if (jsoint->cint.c_int64 < 0) + return 0; + return (uint64_t)jsoint->cint.c_int64; + case json_object_int_type_uint64: return jsoint->cint.c_uint64; + default: json_abort("invalid cint_type"); + } + } + case json_type_double: + // UINT64_MAX can't be exactly represented as a double + // so cast to tell the compiler it's ok to round up. + if (JC_DOUBLE_C(jso)->c_double >= (double)UINT64_MAX) + return UINT64_MAX; + if (JC_DOUBLE_C(jso)->c_double < 0) + return 0; + return (uint64_t)JC_DOUBLE_C(jso)->c_double; + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_string: + if (json_parse_uint64(get_string_component(jso), &cuint) == 0) + return cuint; + /* FALLTHRU */ + default: return 0; + } +} + +int json_object_set_int64(struct json_object *jso, int64_t new_value) +{ + if (!jso || jso->o_type != json_type_int) + return 0; + JC_INT(jso)->cint.c_int64 = new_value; + JC_INT(jso)->cint_type = json_object_int_type_int64; + return 1; +} + +int json_object_set_uint64(struct json_object *jso, uint64_t new_value) +{ + if (!jso || jso->o_type != json_type_int) + return 0; + JC_INT(jso)->cint.c_uint64 = new_value; + JC_INT(jso)->cint_type = json_object_int_type_uint64; + return 1; +} + +int json_object_int_inc(struct json_object *jso, int64_t val) +{ + struct json_object_int *jsoint; + if (!jso || jso->o_type != json_type_int) + return 0; + jsoint = JC_INT(jso); + switch (jsoint->cint_type) + { + case json_object_int_type_int64: + if (val > 0 && jsoint->cint.c_int64 > INT64_MAX - val) + { + jsoint->cint.c_uint64 = (uint64_t)jsoint->cint.c_int64 + (uint64_t)val; + jsoint->cint_type = json_object_int_type_uint64; + } + else if (val < 0 && jsoint->cint.c_int64 < INT64_MIN - val) + { + jsoint->cint.c_int64 = INT64_MIN; + } + else + { + jsoint->cint.c_int64 += val; + } + return 1; + case json_object_int_type_uint64: + if (val > 0 && jsoint->cint.c_uint64 > UINT64_MAX - (uint64_t)val) + { + jsoint->cint.c_uint64 = UINT64_MAX; + } + else if (val < 0 && jsoint->cint.c_uint64 < (uint64_t)(-val)) + { + jsoint->cint.c_int64 = (int64_t)jsoint->cint.c_uint64 + val; + jsoint->cint_type = json_object_int_type_int64; + } + else if (val < 0 && jsoint->cint.c_uint64 >= (uint64_t)(-val)) + { + jsoint->cint.c_uint64 -= (uint64_t)(-val); + } + else + { + jsoint->cint.c_uint64 += val; + } + return 1; + default: json_abort("invalid cint_type"); + } +} + +/* json_object_double */ + +#if defined(HAVE___THREAD) +// i.e. __thread or __declspec(thread) +static SPEC___THREAD char *tls_serialization_float_format = NULL; +#endif +static char *global_serialization_float_format = NULL; + +int json_c_set_serialization_double_format(const char *double_format, int global_or_thread) +{ + if (global_or_thread == JSON_C_OPTION_GLOBAL) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } +#endif + if (global_serialization_float_format) + free(global_serialization_float_format); + if (double_format) + { + char *p = strdup(double_format); + if (p == NULL) + { + _json_c_set_last_err("json_c_set_serialization_double_format: " + "out of memory\n"); + return -1; + } + global_serialization_float_format = p; + } + else + { + global_serialization_float_format = NULL; + } + } + else if (global_or_thread == JSON_C_OPTION_THREAD) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } + if (double_format) + { + char *p = strdup(double_format); + if (p == NULL) + { + _json_c_set_last_err("json_c_set_serialization_double_format: " + "out of memory\n"); + return -1; + } + tls_serialization_float_format = p; + } + else + { + tls_serialization_float_format = NULL; + } +#else + _json_c_set_last_err("json_c_set_serialization_double_format: not compiled " + "with __thread support\n"); + return -1; +#endif + } + else + { + _json_c_set_last_err("json_c_set_serialization_double_format: invalid " + "global_or_thread value: %d\n", global_or_thread); + return -1; + } + return 0; +} + +static int json_object_double_to_json_string_format(struct json_object *jso, struct printbuf *pb, + int level, int flags, const char *format) +{ + struct json_object_double *jsodbl = JC_DOUBLE(jso); + char buf[128], *p, *q; + int size; + /* Although JSON RFC does not support + * NaN or Infinity as numeric values + * ECMA 262 section 9.8.1 defines + * how to handle these cases as strings + */ + if (isnan(jsodbl->c_double)) + { + size = snprintf(buf, sizeof(buf), "NaN"); + } + else if (isinf(jsodbl->c_double)) + { + if (jsodbl->c_double > 0) + size = snprintf(buf, sizeof(buf), "Infinity"); + else + size = snprintf(buf, sizeof(buf), "-Infinity"); + } + else + { + const char *std_format = "%.17g"; + int format_drops_decimals = 0; + int looks_numeric = 0; + + if (!format) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + format = tls_serialization_float_format; + else +#endif + if (global_serialization_float_format) + format = global_serialization_float_format; + else + format = std_format; + } + size = snprintf(buf, sizeof(buf), format, jsodbl->c_double); + + if (size < 0) + return -1; + + p = strchr(buf, ','); + if (p) + *p = '.'; + else + p = strchr(buf, '.'); + + if (format == std_format || strstr(format, ".0f") == NULL) + format_drops_decimals = 1; + + looks_numeric = /* Looks like *some* kind of number */ + is_plain_digit(buf[0]) || (size > 1 && buf[0] == '-' && is_plain_digit(buf[1])); + + if (size < (int)sizeof(buf) - 2 && looks_numeric && !p && /* Has no decimal point */ + strchr(buf, 'e') == NULL && /* Not scientific notation */ + format_drops_decimals) + { + // Ensure it looks like a float, even if snprintf didn't, + // unless a custom format is set to omit the decimal. + strcat(buf, ".0"); + size += 2; + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) + { + /* last useful digit, always keep 1 zero */ + p++; + for (q = p; *q; q++) + { + if (*q != '0') + p = q; + } + /* drop trailing zeroes */ + if (*p != 0) + *(++p) = 0; + size = p - buf; + } + } + // although unlikely, snprintf can fail + if (size < 0) + return -1; + + if (size >= (int)sizeof(buf)) + // The standard formats are guaranteed not to overrun the buffer, + // but if a custom one happens to do so, just silently truncate. + size = sizeof(buf) - 1; + printbuf_memappend(pb, buf, size); + return size; +} + +static int json_object_double_to_json_string_default(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, NULL); +} + +int json_object_double_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + (const char *)jso->_userdata); +} + +struct json_object *json_object_new_double(double d) +{ + struct json_object_double *jso = JSON_OBJECT_NEW(double); + if (!jso) + return NULL; + jso->base._to_json_string = &json_object_double_to_json_string_default; + jso->c_double = d; + return &jso->base; +} + +struct json_object *json_object_new_double_s(double d, const char *ds) +{ + char *new_ds; + struct json_object *jso = json_object_new_double(d); + if (!jso) + return NULL; + + new_ds = strdup(ds); + if (!new_ds) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + json_object_set_serializer(jso, _json_object_userdata_to_json_string, new_ds, + json_object_free_userdata); + return jso; +} + +/* + * A wrapper around json_object_userdata_to_json_string() used only + * by json_object_new_double_s() just so json_object_set_double() can + * detect when it needs to reset the serializer to the default. + */ +static int _json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + return json_object_userdata_to_json_string(jso, pb, level, flags); +} + +int json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + int userdata_len = strlen((const char *)jso->_userdata); + printbuf_memappend(pb, (const char *)jso->_userdata, userdata_len); + return userdata_len; +} + +void json_object_free_userdata(struct json_object *jso, void *userdata) +{ + free(userdata); +} + +double json_object_get_double(const struct json_object *jso) +{ + double cdouble; + char *errPtr = NULL; + + if (!jso) + return 0.0; + switch (jso->o_type) + { + case json_type_double: return JC_DOUBLE_C(jso)->c_double; + case json_type_int: + switch (JC_INT_C(jso)->cint_type) + { + case json_object_int_type_int64: return JC_INT_C(jso)->cint.c_int64; + case json_object_int_type_uint64: return JC_INT_C(jso)->cint.c_uint64; + default: json_abort("invalid cint_type"); + } + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_string: + errno = 0; + cdouble = strtod(get_string_component(jso), &errPtr); + + /* if conversion stopped at the first character, return 0.0 */ + if (errPtr == get_string_component(jso)) + { + errno = EINVAL; + return 0.0; + } + + /* + * Check that the conversion terminated on something sensible + * + * For example, { "pay" : 123AB } would parse as 123. + */ + if (*errPtr != '\0') + { + errno = EINVAL; + return 0.0; + } + + /* + * If strtod encounters a string which would exceed the + * capacity of a double, it returns +/- HUGE_VAL and sets + * errno to ERANGE. But +/- HUGE_VAL is also a valid result + * from a conversion, so we need to check errno. + * + * Underflow also sets errno to ERANGE, but it returns 0 in + * that case, which is what we will return anyway. + * + * See CERT guideline ERR30-C + */ + if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && (ERANGE == errno)) + cdouble = 0.0; + return cdouble; + default: errno = EINVAL; return 0.0; + } +} + +int json_object_set_double(struct json_object *jso, double new_value) +{ + if (!jso || jso->o_type != json_type_double) + return 0; + JC_DOUBLE(jso)->c_double = new_value; + if (jso->_to_json_string == &_json_object_userdata_to_json_string) + json_object_set_serializer(jso, NULL, NULL, NULL); + return 1; +} + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + ssize_t len = JC_STRING(jso)->len; + printbuf_strappend(pb, "\""); + json_escape_str(pb, get_string_component(jso), len < 0 ? -(ssize_t)len : len, flags); + printbuf_strappend(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object *jso) +{ + if (JC_STRING(jso)->len < 0) + free(JC_STRING(jso)->c_string.pdata); + json_object_generic_delete(jso); +} + +static struct json_object *_json_object_new_string(const char *s, const size_t len) +{ + size_t objsize; + struct json_object_string *jso; + + /* + * Structures Actual memory layout + * ------------------- -------------------- + * [json_object_string [json_object_string + * [json_object] [json_object] + * ...other fields... ...other fields... + * c_string] len + * bytes + * of + * string + * data + * \0] + */ + if (len > (SSIZE_T_MAX - (sizeof(*jso) - sizeof(jso->c_string)) - 1)) + return NULL; + objsize = (sizeof(*jso) - sizeof(jso->c_string)) + len + 1; + if (len < sizeof(void *)) + // We need a minimum size to support json_object_set_string() mutability + // so we can stuff a pointer into pdata :( + objsize += sizeof(void *) - len; + + jso = (struct json_object_string *)json_object_new(json_type_string, objsize, + &json_object_string_to_json_string); + + if (!jso) + return NULL; + jso->len = len; + memcpy(jso->c_string.idata, s, len); + // Cast below needed for Clang UB sanitizer + ((char *)jso->c_string.idata)[len] = '\0'; + return &jso->base; +} + +struct json_object *json_object_new_string(const char *s) +{ + return _json_object_new_string(s, strlen(s)); +} + +struct json_object *json_object_new_string_len(const char *s, const int len) +{ + return _json_object_new_string(s, len); +} + +const char *json_object_get_string(struct json_object *jso) +{ + if (!jso) + return NULL; + switch (jso->o_type) + { + case json_type_string: return get_string_component(jso); + default: return json_object_to_json_string(jso); + } +} + +static inline ssize_t _json_object_get_string_len(const struct json_object_string *jso) +{ + ssize_t len; + len = jso->len; + return (len < 0) ? -(ssize_t)len : len; +} +int json_object_get_string_len(const struct json_object *jso) +{ + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_string: return _json_object_get_string_len(JC_STRING_C(jso)); + default: return 0; + } +} + +static int _json_object_set_string_len(json_object *jso, const char *s, size_t len) +{ + char *dstbuf; + ssize_t curlen; + ssize_t newlen; + if (jso == NULL || jso->o_type != json_type_string) + return 0; + + if (len >= INT_MAX - 1) + // jso->len is a signed ssize_t, so it can't hold the + // full size_t range. json_object_get_string_len returns + // length as int, cap length at INT_MAX. + return 0; + + dstbuf = get_string_component_mutable(jso); + curlen = JC_STRING(jso)->len; + if (curlen < 0) + curlen = -curlen; + newlen = len; + + if ((ssize_t)len > curlen) + { + // We have no way to return the new ptr from realloc(jso, newlen) + // and we have no way of knowing whether there's extra room available + // so we need to stuff a pointer in to pdata :( + dstbuf = (char *)malloc(len + 1); + if (dstbuf == NULL) + return 0; + if (JC_STRING(jso)->len < 0) + free(JC_STRING(jso)->c_string.pdata); + JC_STRING(jso)->c_string.pdata = dstbuf; + newlen = -(ssize_t)len; + } + else if (JC_STRING(jso)->len < 0) + { + // We've got enough room in the separate allocated buffer, + // so use it as-is and continue to indicate that pdata is used. + newlen = -(ssize_t)len; + } + + memcpy(dstbuf, (const void *)s, len); + dstbuf[len] = '\0'; + JC_STRING(jso)->len = newlen; + return 1; +} + +int json_object_set_string(json_object *jso, const char *s) +{ + return _json_object_set_string_len(jso, s, strlen(s)); +} + +int json_object_set_string_len(json_object *jso, const char *s, int len) +{ + return _json_object_set_string_len(jso, s, len); +} + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + int had_children = 0; + size_t ii; + + printbuf_strappend(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + for (ii = 0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + printbuf_strappend(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if (val == NULL) + printbuf_strappend(pb, "null"); + else if (val->_to_json_string(val, pb, level + 1, flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb, level, flags); + } + + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + return printbuf_strappend(pb, " ]"); + return printbuf_strappend(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object *)data); +} + +static void json_object_array_delete(struct json_object *jso) +{ + array_list_free(JC_ARRAY(jso)->c_array); + json_object_generic_delete(jso); +} + +struct json_object *json_object_new_array(void) +{ + return json_object_new_array_ext(ARRAY_LIST_DEFAULT_SIZE); +} +struct json_object *json_object_new_array_ext(int initial_size) +{ + struct json_object_array *jso = JSON_OBJECT_NEW(array); + if (!jso) + return NULL; + jso->c_array = array_list_new2(&json_object_array_entry_free, initial_size); + if (jso->c_array == NULL) + { + free(jso); + return NULL; + } + return &jso->base; +} + +struct array_list *json_object_get_array(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch (jso->o_type) + { + case json_type_array: return JC_ARRAY_C(jso)->c_array; + default: return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, int (*sort_fn)(const void *, const void *)) +{ + assert(json_object_get_type(jso) == json_type_array); + array_list_sort(JC_ARRAY(jso)->c_array, sort_fn); +} + +struct json_object *json_object_array_bsearch(const struct json_object *key, + const struct json_object *jso, + int (*sort_fn)(const void *, const void *)) +{ + struct json_object **result; + + assert(json_object_get_type(jso) == json_type_array); + result = (struct json_object **)array_list_bsearch((const void **)(void *)&key, + JC_ARRAY_C(jso)->c_array, sort_fn); + + if (!result) + return NULL; + return *result; +} + +size_t json_object_array_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_length(JC_ARRAY_C(jso)->c_array); +} + +int json_object_array_add(struct json_object *jso, struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_add(JC_ARRAY(jso)->c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, size_t idx, struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_put_idx(JC_ARRAY(jso)->c_array, idx, val); +} + +int json_object_array_del_idx(struct json_object *jso, size_t idx, size_t count) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_del_idx(JC_ARRAY(jso)->c_array, idx, count); +} + +struct json_object *json_object_array_get_idx(const struct json_object *jso, size_t idx) +{ + assert(json_object_get_type(jso) == json_type_array); + return (struct json_object *)array_list_get_idx(JC_ARRAY_C(jso)->c_array, idx); +} + +static int json_array_equal(struct json_object *jso1, struct json_object *jso2) +{ + size_t len, i; + + len = json_object_array_length(jso1); + if (len != json_object_array_length(jso2)) + return 0; + + for (i = 0; i < len; i++) + { + if (!json_object_equal(json_object_array_get_idx(jso1, i), + json_object_array_get_idx(jso2, i))) + return 0; + } + return 1; +} + +int json_object_array_shrink(struct json_object *jso, int empty_slots) +{ + if (empty_slots < 0) + json_abort("json_object_array_shrink called with negative empty_slots"); + return array_list_shrink(JC_ARRAY(jso)->c_array, empty_slots); +} + +struct json_object *json_object_new_null(void) +{ + return NULL; +} + +static int json_object_all_values_equal(struct json_object *jso1, struct json_object *jso2) +{ + struct json_object_iter iter; + struct json_object *sub; + + assert(json_object_get_type(jso1) == json_type_object); + assert(json_object_get_type(jso2) == json_type_object); + /* Iterate over jso1 keys and see if they exist and are equal in jso2 */ + json_object_object_foreachC(jso1, iter) + { + if (!lh_table_lookup_ex(JC_OBJECT(jso2)->c_object, (void *)iter.key, + (void **)(void *)&sub)) + return 0; + if (!json_object_equal(iter.val, sub)) + return 0; + } + + /* Iterate over jso2 keys to see if any exist that are not in jso1 */ + json_object_object_foreachC(jso2, iter) + { + if (!lh_table_lookup_ex(JC_OBJECT(jso1)->c_object, (void *)iter.key, + (void **)(void *)&sub)) + return 0; + } + + return 1; +} + +int json_object_equal(struct json_object *jso1, struct json_object *jso2) +{ + if (jso1 == jso2) + return 1; + + if (!jso1 || !jso2) + return 0; + + if (jso1->o_type != jso2->o_type) + return 0; + + switch (jso1->o_type) + { + case json_type_boolean: return (JC_BOOL(jso1)->c_boolean == JC_BOOL(jso2)->c_boolean); + + case json_type_double: return (JC_DOUBLE(jso1)->c_double == JC_DOUBLE(jso2)->c_double); + + case json_type_int: + { + struct json_object_int *int1 = JC_INT(jso1); + struct json_object_int *int2 = JC_INT(jso2); + if (int1->cint_type == json_object_int_type_int64) + { + if (int2->cint_type == json_object_int_type_int64) + return (int1->cint.c_int64 == int2->cint.c_int64); + if (int1->cint.c_int64 < 0) + return 0; + return ((uint64_t)int1->cint.c_int64 == int2->cint.c_uint64); + } + // else jso1 is a uint64 + if (int2->cint_type == json_object_int_type_uint64) + return (int1->cint.c_uint64 == int2->cint.c_uint64); + if (int2->cint.c_int64 < 0) + return 0; + return (int1->cint.c_uint64 == (uint64_t)int2->cint.c_int64); + } + + case json_type_string: + { + return (_json_object_get_string_len(JC_STRING(jso1)) == + _json_object_get_string_len(JC_STRING(jso2)) && + memcmp(get_string_component(jso1), get_string_component(jso2), + _json_object_get_string_len(JC_STRING(jso1))) == 0); + } + + case json_type_object: return json_object_all_values_equal(jso1, jso2); + + case json_type_array: return json_array_equal(jso1, jso2); + + case json_type_null: return 1; + }; + + return 0; +} + +static int json_object_copy_serializer_data(struct json_object *src, struct json_object *dst) +{ + if (!src->_userdata && !src->_user_delete) + return 0; + + if (dst->_to_json_string == json_object_userdata_to_json_string || + dst->_to_json_string == _json_object_userdata_to_json_string) + { + char *p; + assert(src->_userdata); + p = strdup(src->_userdata); + if (p == NULL) + { + _json_c_set_last_err("json_object_copy_serializer_data: out of memory\n"); + return -1; + } + dst->_userdata = p; + } + // else if ... other supported serializers ... + else + { + _json_c_set_last_err( + "json_object_copy_serializer_data: unable to copy unknown serializer data: " + "%p\n", (void *)dst->_to_json_string); + return -1; + } + dst->_user_delete = src->_user_delete; + return 0; +} + +/** + * The default shallow copy implementation. Simply creates a new object of the same + * type but does *not* copy over _userdata nor retain any custom serializer. + * If custom serializers are in use, json_object_deep_copy() must be passed a shallow copy + * implementation that is aware of how to copy them. + * + * This always returns -1 or 1. It will never return 2 since it does not copy the serializer. + */ +int json_c_shallow_copy_default(json_object *src, json_object *parent, const char *key, + size_t index, json_object **dst) +{ + switch (src->o_type) + { + case json_type_boolean: *dst = json_object_new_boolean(JC_BOOL(src)->c_boolean); break; + + case json_type_double: *dst = json_object_new_double(JC_DOUBLE(src)->c_double); break; + + case json_type_int: + switch (JC_INT(src)->cint_type) + { + case json_object_int_type_int64: + *dst = json_object_new_int64(JC_INT(src)->cint.c_int64); + break; + case json_object_int_type_uint64: + *dst = json_object_new_uint64(JC_INT(src)->cint.c_uint64); + break; + default: json_abort("invalid cint_type"); + } + break; + + case json_type_string: + *dst = json_object_new_string_len(get_string_component(src), + _json_object_get_string_len(JC_STRING(src))); + break; + + case json_type_object: *dst = json_object_new_object(); break; + + case json_type_array: *dst = json_object_new_array(); break; + + default: errno = EINVAL; return -1; + } + + if (!*dst) + { + errno = ENOMEM; + return -1; + } + (*dst)->_to_json_string = src->_to_json_string; + // _userdata and _user_delete are copied later + return 1; +} + +/* + * The actual guts of json_object_deep_copy(), with a few additional args + * needed so we can keep track of where we are within the object tree. + * + * Note: caller is responsible for freeing *dst if this fails and returns -1. + */ +static int json_object_deep_copy_recursive(struct json_object *src, struct json_object *parent, + const char *key_in_parent, size_t index_in_parent, + struct json_object **dst, + json_c_shallow_copy_fn *shallow_copy) +{ + struct json_object_iter iter; + size_t src_array_len, ii; + + int shallow_copy_rc = 0; + shallow_copy_rc = shallow_copy(src, parent, key_in_parent, index_in_parent, dst); + /* -1=error, 1=object created ok, 2=userdata set */ + if (shallow_copy_rc < 1) + { + errno = EINVAL; + return -1; + } + assert(*dst != NULL); + + switch (src->o_type) + { + case json_type_object: + json_object_object_foreachC(src, iter) + { + struct json_object *jso = NULL; + /* This handles the `json_type_null` case */ + if (!iter.val) + jso = NULL; + else if (json_object_deep_copy_recursive(iter.val, src, iter.key, UINT_MAX, + &jso, shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_object_add(*dst, iter.key, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + case json_type_array: + src_array_len = json_object_array_length(src); + for (ii = 0; ii < src_array_len; ii++) + { + struct json_object *jso = NULL; + struct json_object *jso1 = json_object_array_get_idx(src, ii); + /* This handles the `json_type_null` case */ + if (!jso1) + jso = NULL; + else if (json_object_deep_copy_recursive(jso1, src, NULL, ii, &jso, + shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_array_add(*dst, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + default: + break; + /* else, nothing to do, shallow_copy already did. */ + } + + if (shallow_copy_rc != 2) + return json_object_copy_serializer_data(src, *dst); + + return 0; +} + +int json_object_deep_copy(struct json_object *src, struct json_object **dst, + json_c_shallow_copy_fn *shallow_copy) +{ + int rc; + + /* Check if arguments are sane ; *dst must not point to a non-NULL object */ + if (!src || !dst || *dst) + { + errno = EINVAL; + return -1; + } + + if (shallow_copy == NULL) + shallow_copy = json_c_shallow_copy_default; + + rc = json_object_deep_copy_recursive(src, NULL, NULL, UINT_MAX, dst, shallow_copy); + if (rc < 0) + { + json_object_put(*dst); + *dst = NULL; + } + + return rc; +} + +static void json_abort(const char *message) +{ + if (message != NULL) + fprintf(stderr, "json-c aborts with error: %s\n", message); + abort(); +} diff --git a/comm/third_party/json-c/json_object.h b/comm/third_party/json-c/json_object.h new file mode 100644 index 0000000000..e22392f39c --- /dev/null +++ b/comm/third_party/json-c/json_object.h @@ -0,0 +1,1077 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Core json-c API. Start here, or with json_tokener.h + */ +#ifndef _json_object_h_ +#define _json_object_h_ + +#ifdef __GNUC__ +#define JSON_C_CONST_FUNCTION(func) func __attribute__((const)) +#else +#define JSON_C_CONST_FUNCTION(func) func +#endif + +#include "json_inttypes.h" +#include "json_types.h" +#include "printbuf.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1 << 0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at https://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1 << 1) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * Instead of a "Two Space Tab" this gives a single tab character. + */ +#define JSON_C_TO_STRING_PRETTY_TAB (1 << 3) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1 << 2) + +/** + * Don't escape forward slashes. + */ +#define JSON_C_TO_STRING_NOSLASHESCAPE (1 << 4) + +/** + * A flag for the json_object_object_add_ex function which + * causes the value to be added without a check if it already exists. + * Note: it is the responsibility of the caller to ensure that no + * key is added multiple times. If this is done, results are + * unpredictable. While this option is somewhat dangerous, it + * permits potentially large performance savings in code that + * knows for sure the key values are unique (e.g. because the + * code adds a well-known set of constant key values). + */ +#define JSON_C_OBJECT_ADD_KEY_IS_NEW (1 << 1) +/** + * A flag for the json_object_object_add_ex function which + * flags the key as being constant memory. This means that + * the key will NOT be copied via strdup(), resulting in a + * potentially huge performance win (malloc, strdup and + * free are usually performance hogs). It is acceptable to + * use this flag for keys in non-constant memory blocks if + * the caller ensure that the memory holding the key lives + * longer than the corresponding json object. However, this + * is somewhat dangerous and should only be done if really + * justified. + * The general use-case for this flag is cases where the + * key is given as a real constant value in the function + * call, e.g. as in + * json_object_object_add_ex(obj, "ip", json, + * JSON_C_OBJECT_ADD_CONSTANT_KEY); + */ +#define JSON_C_OBJECT_ADD_CONSTANT_KEY (1 << 2) +/** + * This flag is an alias to JSON_C_OBJECT_ADD_CONSTANT_KEY. + * Historically, this flag was used first and the new name + * JSON_C_OBJECT_ADD_CONSTANT_KEY was introduced for version + * 0.16.00 in order to have regular naming. + * Use of this flag is now legacy. + */ +#define JSON_C_OBJECT_KEY_IS_CONSTANT JSON_C_OBJECT_ADD_CONSTANT_KEY + +/** + * Set the global value of an option, which will apply to all + * current and future threads that have not set a thread-local value. + * + * @see json_c_set_serialization_double_format + */ +#define JSON_C_OPTION_GLOBAL (0) +/** + * Set a thread-local value of an option, overriding the global value. + * This will fail if json-c is not compiled with threading enabled, and + * with the __thread specifier (or equivalent) available. + * + * @see json_c_set_serialization_double_format + */ +#define JSON_C_OPTION_THREAD (1) + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby taking ownership of it. + * + * Cases where you might need to increase the refcount include: + * - Using an object field or array index (retrieved through + * `json_object_object_get()` or `json_object_array_get_idx()`) + * beyond the lifetime of the parent object. + * - Detaching an object field or array index from its parent object + * (using `json_object_object_del()` or `json_object_array_del_idx()`) + * - Sharing a json_object with multiple (not necessarily parallel) threads + * of execution that all expect to free it (with `json_object_put()`) when + * they're done. + * + * @param obj the json_object instance + * @see json_object_put() + * @see json_object_object_get() + * @see json_object_array_get_idx() + */ +JSON_EXPORT struct json_object *json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count, leading to a classic use-after-free bug. + * In particular, you normally do not need to call `json_object_put()` on the + * json_object returned by `json_object_object_get()` or `json_object_array_get_idx()`. + * + * Just like after calling `free()` on a block of memory, you must not use + * `obj` after calling `json_object_put()` on it or any object that it + * is a member of (unless you know you've called `json_object_get(obj)` to + * explicitly increment the refcount). + * + * NULL may be passed, which which case this is a no-op. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + * @see json_object_get() + */ +JSON_EXPORT int json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string + */ +JSON_EXPORT int json_object_is_type(const struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string + */ +JSON_EXPORT enum json_type json_object_get_type(const struct json_object *obj); + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * The pointer you get is an internal of your json object. You don't + * have to free it, later use of json_object_put() should be sufficient. + * If you can not ensure there's no concurrent access to *obj use + * strdup(). + * @param obj the json_object instance + * @returns a string in JSON format + */ +JSON_EXPORT const char *json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @see json_object_to_json_string() for details on how to free string. + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +JSON_EXPORT const char *json_object_to_json_string_ext(struct json_object *obj, int flags); + +/** Stringify object to json format + * @see json_object_to_json_string() for details on how to free string. + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @param length a pointer where, if not NULL, the length (without null) is stored + * @returns a string in JSON format and the length if not NULL + */ +JSON_EXPORT const char *json_object_to_json_string_length(struct json_object *obj, int flags, + size_t *length); + +/** + * Returns the userdata set by json_object_set_userdata() or + * json_object_set_serializer() + * + * @param jso the object to return the userdata for + */ +JSON_EXPORT void *json_object_get_userdata(json_object *jso); + +/** + * Set an opaque userdata value for an object + * + * The userdata can be retrieved using json_object_get_userdata(). + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * Note: Objects created by parsing strings may have custom serializers set + * which expect the userdata to contain specific data (due to use of + * json_object_new_double_s()). In this case, json_object_set_serialiser() with + * NULL as to_string_func should be used instead to set the userdata and reset + * the serializer to its default value. + * + * @param jso the object to set the userdata for + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +JSON_EXPORT void json_object_set_userdata(json_object *jso, void *userdata, + json_object_delete_fn *user_delete); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * If to_string_func is NULL the default behaviour is reset (but the userdata + * and user_delete fields are still set). + * + * The userdata parameter is optional and may be passed as NULL. It can be used + * to provide additional data for to_string_func to use. This parameter may + * be NULL even if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * Note that the userdata is the same as set by json_object_set_userdata(), so + * care must be taken not to overwrite the value when both a custom serializer + * and json_object_set_userdata() are used. + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +JSON_EXPORT void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn *to_string_func, + void *userdata, json_object_delete_fn *user_delete); + +#ifdef __clang__ +/* + * Clang doesn't pay attention to the parameters defined in the + * function typedefs used here, so turn off spurious doc warnings. + * { + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" +#endif + +/** + * Simply call free on the userdata pointer. + * Can be used with json_object_set_serializer(). + * + * @param jso unused + * @param userdata the pointer that is passed to free(). + */ +JSON_EXPORT json_object_delete_fn json_object_free_userdata; + +/** + * Copy the jso->_userdata string over to pb as-is. + * Can be used with json_object_set_serializer(). + * + * @param jso The object whose _userdata is used. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +JSON_EXPORT json_object_to_json_string_fn json_object_userdata_to_json_string; + +#ifdef __clang__ +/* } */ +#pragma clang diagnostic pop +#endif + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +JSON_EXPORT struct json_object *json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +JSON_EXPORT struct lh_table *json_object_get_object(const struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +JSON_EXPORT int json_object_object_length(const struct json_object *obj); + +/** Get the sizeof (struct json_object). + * @returns a size_t with the sizeof (struct json_object) + */ +JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count of `val` will *not* be incremented, in effect + * transferring ownership that object to `obj`, and thus `val` will be + * freed when `obj` is. (i.e. through `json_object_put(obj)`) + * + * If you want to retain a reference to the added object, independent + * of the lifetime of obj, you must increment the refcount with + * `json_object_get(val)` (and later release it with json_object_put()). + * + * Since ownership transfers to `obj`, you must make sure + * that you do in fact have ownership over `val`. For instance, + * json_object_new_object() will give you ownership until you transfer it, + * whereas json_object_object_get() does not. + * + * Any previous object stored under `key` in `obj` will have its refcount + * decremented, and be freed normally if that drops to zero. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, + struct json_object *val); + +/** Add an object field to a json_object of type json_type_object + * + * The semantics are identical to json_object_object_add, except that an + * additional flag fields gives you more control over some detail aspects + * of processing. See the description of JSON_C_OBJECT_ADD_* flags for more + * details. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + * @param opts process-modifying options. To specify multiple options, use + * (OPT1|OPT2) + */ +JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key, + struct json_object *const val, const unsigned opts); + +/** Get the json_object associate with a given object field. + * Deprecated/discouraged: used json_object_object_get_ex instead. + * + * This returns NULL if the field is found but its value is null, or if + * the field is not found, or if obj is not a json_type_object. If you + * need to distinguish between these cases, use json_object_object_get_ex(). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + */ +JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj, + const char *key); + +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) + +#define json_object_object_foreach(obj, key, val) \ + char *key = NULL; \ + struct json_object *val __attribute__((__unused__)) = NULL; \ + for (struct lh_entry *entry##key = lh_table_head(json_object_get_object(obj)), \ + *entry_next##key = NULL; \ + ({ \ + if (entry##key) \ + { \ + key = (char *)lh_entry_k(entry##key); \ + val = (struct json_object *)lh_entry_v(entry##key); \ + entry_next##key = lh_entry_next(entry##key); \ + }; \ + entry##key; \ + }); \ + entry##key = entry_next##key) + +#else /* ANSI C or MSC */ + +#define json_object_object_foreach(obj, key, val) \ + char *key = NULL; \ + struct json_object *val = NULL; \ + struct lh_entry *entry##key; \ + struct lh_entry *entry_next##key = NULL; \ + for (entry##key = lh_table_head(json_object_get_object(obj)); \ + (entry##key ? (key = (char *)lh_entry_k(entry##key), \ + val = (struct json_object *)lh_entry_v(entry##key), \ + entry_next##key = lh_entry_next(entry##key), entry##key) \ + : 0); \ + entry##key = entry_next##key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator, use type json_object_iter + */ +#define json_object_object_foreachC(obj, iter) \ + for (iter.entry = lh_table_head(json_object_get_object(obj)); \ + (iter.entry ? (iter.key = (char *)lh_entry_k(iter.entry), \ + iter.val = (struct json_object *)lh_entry_v(iter.entry), iter.entry) \ + : 0); \ + iter.entry = lh_entry_next(iter.entry)) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * with 32 slots allocated. + * If you know the array size you'll need ahead of time, use + * json_object_new_array_ext() instead. + * @see json_object_new_array_ext() + * @see json_object_array_shrink() + * @returns a json_object of type json_type_array + */ +JSON_EXPORT struct json_object *json_object_new_array(void); + +/** Create a new empty json_object of type json_type_array + * with the desired number of slots allocated. + * @see json_object_array_shrink() + * @param initial_size the number of slots to allocate + * @returns a json_object of type json_type_array + */ +JSON_EXPORT struct json_object *json_object_new_array_ext(int initial_size); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +JSON_EXPORT struct array_list *json_object_get_array(const struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +JSON_EXPORT size_t json_object_array_length(const struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to sort_fn +* +* @param jso the json_object instance +* @param sort_fn a sorting function +*/ +JSON_EXPORT void json_object_array_sort(struct json_object *jso, + int (*sort_fn)(const void *, const void *)); + +/** Binary search a sorted array for a specified key object. + * + * It depends on your compare function what's sufficient as a key. + * Usually you create some dummy object with the parameter compared in + * it, to identify the right item you're actually looking for. + * + * @see json_object_array_sort() for hints on the compare function. + * + * @param key a dummy json_object with the right key + * @param jso the array object we're searching + * @param sort_fn the sort/compare function + * + * @return the wanted json_object instance + */ +JSON_EXPORT struct json_object * +json_object_array_bsearch(const struct json_object *key, const struct json_object *jso, + int (*sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +JSON_EXPORT int json_object_array_add(struct json_object *obj, struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +JSON_EXPORT int json_object_array_put_idx(struct json_object *obj, size_t idx, + struct json_object *val); + +/** Get the element at specified index of array `obj` (which must be a json_object of type json_type_array) + * + * *No* reference counts will be changed, and ownership of the returned + * object remains with `obj`. See json_object_object_get() for additional + * implications of this behavior. + * + * Calling this with anything other than a json_type_array will trigger + * an assert. + * + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +JSON_EXPORT struct json_object *json_object_array_get_idx(const struct json_object *obj, + size_t idx); + +/** Delete an elements from a specified index in an array (a json_object of type json_type_array) + * + * The reference count will be decremented for each of the deleted objects. If there + * are no more owners of an element that is being deleted, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param idx the index to start deleting elements at + * @param count the number of elements to delete + * @returns 0 if the elements were successfully deleted + */ +JSON_EXPORT int json_object_array_del_idx(struct json_object *obj, size_t idx, size_t count); + +/** + * Shrink the internal memory allocation of the array to just + * enough to fit the number of elements in it, plus empty_slots. + * + * @param jso the json_object instance, must be json_type_array + * @param empty_slots the number of empty slots to leave allocated + */ +JSON_EXPORT int json_object_array_shrink(struct json_object *jso, int empty_slots); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool 1 or 0 + * @returns a json_object of type json_type_boolean + */ +JSON_EXPORT struct json_object *json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return 0 if there value is zero + * or 1 otherwise. If the passed object is a string it will return + * 1 if it has a non zero length. + * If any other object type is passed 0 will be returned, even non-empty + * json_type_array and json_type_object objects. + * + * @param obj the json_object instance + * @returns a json_bool + */ +JSON_EXPORT json_bool json_object_get_boolean(const struct json_object *obj); + +/** Set the json_bool value of a json_object + * + * The type of obj is checked to be a json_type_boolean and 0 is returned + * if it is not without any further actions. If type of obj is json_type_boolean + * the object value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_boolean(struct json_object *obj, json_bool new_value); + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +JSON_EXPORT struct json_object *json_object_new_int(int32_t i); + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +JSON_EXPORT struct json_object *json_object_new_int64(int64_t i); + +/** Create a new empty json_object of type json_type_uint + * @param i the integer + * @returns a json_object of type json_type_uint + */ +JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i); + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +JSON_EXPORT int32_t json_object_get_int(const struct json_object *obj); + +/** Set the int value of a json_object + * + * The type of obj is checked to be a json_type_int and 0 is returned + * if it is not without any further actions. If type of obj is json_type_int + * the object value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_int(struct json_object *obj, int new_value); + +/** Increment a json_type_int object by the given amount, which may be negative. + * + * If the type of obj is not json_type_int then 0 is returned with no further + * action taken. + * If the addition would result in a overflow, the object value + * is set to INT64_MAX. + * If the addition would result in a underflow, the object value + * is set to INT64_MIN. + * Neither overflow nor underflow affect the return value. + * + * @param obj the json_object instance + * @param val the value to add + * @returns 1 if the increment succeeded, 0 otherwise + */ +JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj); + +/** Get the uint value of a json_object + * + * The type is coerced to a uint64 if the passed object is not a uint64. + * double objects will return their uint64 conversion. Strings will be + * parsed as an uint64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an uint64 + */ +JSON_EXPORT uint64_t json_object_get_uint64(const struct json_object *obj); + +/** Set the int64_t value of a json_object + * + * The type of obj is checked to be a json_type_int and 0 is returned + * if it is not without any further actions. If type of obj is json_type_int + * the object value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_int64(struct json_object *obj, int64_t new_value); + +/** Set the uint64_t value of a json_object + * + * The type of obj is checked to be a json_type_uint and 0 is returned + * if it is not without any further actions. If type of obj is json_type_uint + * the object value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_uint64(struct json_object *obj, uint64_t new_value); + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * + * @see json_object_double_to_json_string() for how to set a custom format string. + * + * @param d the double + * @returns a json_object of type json_type_double + */ +JSON_EXPORT struct json_object *json_object_new_double(double d); + +/** + * Create a new json_object of type json_type_double, using + * the exact serialized representation of the value. + * + * This allows for numbers that would otherwise get displayed + * inefficiently (e.g. 12.3 => "12.300000000000001") to be + * serialized with the more convenient form. + * + * Notes: + * + * This is used by json_tokener_parse_ex() to allow for + * an exact re-serialization of a parsed object. + * + * The userdata field is used to store the string representation, so it + * can't be used for other data if this function is used. + * + * A roughly equivalent sequence of calls, with the difference being that + * the serialization function won't be reset by json_object_set_double(), is: + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(jso, json_object_userdata_to_json_string, + * strdup(ds), json_object_free_userdata); + * @endcode + * + * @param d the numeric value of the double. + * @param ds the string representation of the double. This will be copied. + */ +JSON_EXPORT struct json_object *json_object_new_double_s(double d, const char *ds); + +/** + * Set a global or thread-local json-c option, depending on whether + * JSON_C_OPTION_GLOBAL or JSON_C_OPTION_THREAD is passed. + * Thread-local options default to undefined, and inherit from the global + * value, even if the global value is changed after the thread is created. + * Attempting to set thread-local options when threading is not compiled in + * will result in an error. Be sure to check the return value. + * + * double_format is a "%g" printf format, such as "%.20g" + * + * @return -1 on errors, 0 on success. + */ +JSON_EXPORT int json_c_set_serialization_double_format(const char *double_format, + int global_or_thread); + +/** Serialize a json_object of type json_type_double to a string. + * + * This function isn't meant to be called directly. Instead, you can set a + * custom format string for the serialization of this double using the + * following call (where "%.17g" actually is the default): + * + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(jso, json_object_double_to_json_string, + * "%.17g", NULL); + * @endcode + * + * @see printf(3) man page for format strings + * + * @param jso The json_type_double object that is serialized. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +JSON_EXPORT int json_object_double_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +JSON_EXPORT double json_object_get_double(const struct json_object *obj); + +/** Set the double value of a json_object + * + * The type of obj is checked to be a json_type_double and 0 is returned + * if it is not without any further actions. If type of obj is json_type_double + * the object value is changed to new_value + * + * If the object was created with json_object_new_double_s(), the serialization + * function is reset to the default and the cached serialized value is cleared. + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_double(struct json_object *obj, double new_value); + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + * @see json_object_new_string_len() + */ +JSON_EXPORT struct json_object *json_object_new_string(const char *s); + +/** Create a new empty json_object of type json_type_string and allocate + * len characters for the new string. + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @param len max length of the new string + * @returns a json_object of type json_type_string + * @see json_object_new_string() + */ +JSON_EXPORT struct json_object *json_object_new_string_len(const char *s, const int len); + +/** Get the string value of a json_object + * + * If the passed object is of type json_type_null (i.e. obj == NULL), + * NULL is returned. + * + * If the passed object of type json_type_string, the string contents + * are returned. + * + * Otherwise the JSON representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string or NULL + */ +JSON_EXPORT const char *json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +JSON_EXPORT int json_object_get_string_len(const struct json_object *obj); + +/** Set the string value of a json_object with zero terminated strings + * equivalent to json_object_set_string_len (obj, new_value, strlen(new_value)) + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_string(json_object *obj, const char *new_value); + +/** Set the string value of a json_object str + * + * The type of obj is checked to be a json_type_string and 0 is returned + * if it is not without any further actions. If type of obj is json_type_string + * the object value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set; Since string length is given in len this need not be zero terminated + * @param len the length of new_value + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_string_len(json_object *obj, const char *new_value, int len); + +/** This method exists only to provide a complementary function + * along the lines of the other json_object_new_* functions. + * It always returns NULL, and it is entirely acceptable to simply use NULL directly. + */ +JSON_EXPORT struct json_object *json_object_new_null(void); + +/** Check if two json_object's are equal + * + * If the passed objects are equal 1 will be returned. + * Equality is defined as follows: + * - json_objects of different types are never equal + * - json_objects of the same primitive type are equal if the + * c-representation of their value is equal + * - json-arrays are considered equal if all values at the same + * indices are equal (same order) + * - Complex json_objects are considered equal if all + * contained objects referenced by their key are equal, + * regardless their order. + * + * @param obj1 the first json_object instance + * @param obj2 the second json_object instance + * @returns whether both objects are equal or not + */ +JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object *obj2); + +/** + * Perform a shallow copy of src into *dst as part of an overall json_object_deep_copy(). + * + * If src is part of a containing object or array, parent will be non-NULL, + * and key or index will be provided. + * When shallow_copy is called *dst will be NULL, and must be non-NULL when it returns. + * src will never be NULL. + * + * If shallow_copy sets the serializer on an object, return 2 to indicate to + * json_object_deep_copy that it should not attempt to use the standard userdata + * copy function. + * + * @return On success 1 or 2, -1 on errors + */ +typedef int(json_c_shallow_copy_fn)(json_object *src, json_object *parent, const char *key, + size_t index, json_object **dst); + +/** + * The default shallow copy implementation for use with json_object_deep_copy(). + * This simply calls the appropriate json_object_new_() function and + * copies over the serializer function (_to_json_string internal field of + * the json_object structure) but not any _userdata or _user_delete values. + * + * If you're writing a custom shallow_copy function, perhaps because you're using + * your own custom serializer, you can call this first to create the new object + * before customizing it with json_object_set_serializer(). + * + * @return 1 on success, -1 on errors, but never 2. + */ +JSON_EXPORT json_c_shallow_copy_fn json_c_shallow_copy_default; + +/** + * Copy the contents of the JSON object. + * The destination object must be initialized to NULL, + * to make sure this function won't overwrite an existing JSON object. + * + * This does roughly the same thing as + * `json_tokener_parse(json_object_get_string(src))`. + * + * @param src source JSON object whose contents will be copied + * @param dst pointer to the destination object where the contents of `src`; + * make sure this pointer is initialized to NULL + * @param shallow_copy an optional function to copy individual objects, needed + * when custom serializers are in use. See also + * json_object set_serializer. + * + * @returns 0 if the copy went well, -1 if an error occurred during copy + * or if the destination pointer is non-NULL + */ + +JSON_EXPORT int json_object_deep_copy(struct json_object *src, struct json_object **dst, + json_c_shallow_copy_fn *shallow_copy); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_object_iterator.c b/comm/third_party/json-c/json_object_iterator.c new file mode 100644 index 0000000000..db8488ae1e --- /dev/null +++ b/comm/third_party/json-c/json_object_iterator.c @@ -0,0 +1,153 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +******************************************************************************* +*/ +#include "config.h" + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void *kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator json_object_iter_begin(struct json_object *obj) +{ + struct json_object_iterator iter; + struct lh_table *pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = lh_table_head(pTable); + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator json_object_iter_end(const struct json_object *obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void json_object_iter_next(struct json_object_iterator *iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = lh_entry_next(((const struct lh_entry *)iter->opaque_)); +} + +/** + * **************************************************************************** + */ +const char *json_object_iter_peek_name(const struct json_object_iterator *iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char *)(((const struct lh_entry *)iter->opaque_)->k); +} + +/** + * **************************************************************************** + */ +struct json_object *json_object_iter_peek_value(const struct json_object_iterator *iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object *)lh_entry_v((const struct lh_entry *)iter->opaque_); +} + +/** + * **************************************************************************** + */ +json_bool json_object_iter_equal(const struct json_object_iterator *iter1, + const struct json_object_iterator *iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + +/** + * **************************************************************************** + */ +struct json_object_iterator json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/comm/third_party/json-c/json_object_iterator.h b/comm/third_party/json-c/json_object_iterator.h new file mode 100644 index 0000000000..a9b1433c74 --- /dev/null +++ b/comm/third_party/json-c/json_object_iterator.h @@ -0,0 +1,228 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief An API for iterating over json_type_object objects, +* styled to be familiar to C++ programmers. +* Unlike json_object_object_foreach() and +* json_object_object_foreachC(), this avoids the need to expose +* json-c internals like lh_entry. +* +* API attributes:
+* * Thread-safe: NO
+* * Reentrant: NO +* +******************************************************************************* +*/ + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include "json_types.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator +{ + const void *opaque_; +}; + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +JSON_EXPORT struct json_object_iterator json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +JSON_EXPORT struct json_object_iterator json_object_iter_begin(struct json_object *obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +JSON_EXPORT struct json_object_iterator json_object_iter_end(const struct json_object *obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +JSON_EXPORT void json_object_iter_next(struct json_object_iterator *iter); + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +JSON_EXPORT const char *json_object_iter_peek_name(const struct json_object_iterator *iter); + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +JSON_EXPORT struct json_object * +json_object_iter_peek_value(const struct json_object_iterator *iter); + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +JSON_EXPORT json_bool json_object_iter_equal(const struct json_object_iterator *iter1, + const struct json_object_iterator *iter2); + +#ifdef __cplusplus +} +#endif + +#endif /* JSON_OBJECT_ITERATOR_H */ diff --git a/comm/third_party/json-c/json_object_private.h b/comm/third_party/json-c/json_object_private.h new file mode 100644 index 0000000000..e143b4649a --- /dev/null +++ b/comm/third_party/json-c/json_object_private.h @@ -0,0 +1,107 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct json_object; +#include "json_inttypes.h" +#include "json_types.h" + +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +/* json object int type, support extension*/ +typedef enum json_object_int_type +{ + json_object_int_type_int64, + json_object_int_type_uint64 +} json_object_int_type; + +struct json_object +{ + enum json_type o_type; + uint32_t _ref_count; + json_object_to_json_string_fn *_to_json_string; + struct printbuf *_pb; + json_object_delete_fn *_user_delete; + void *_userdata; + // Actually longer, always malloc'd as some more-specific type. + // The rest of a struct json_object_${o_type} follows +}; + +struct json_object_object +{ + struct json_object base; + struct lh_table *c_object; +}; +struct json_object_array +{ + struct json_object base; + struct array_list *c_array; +}; + +struct json_object_boolean +{ + struct json_object base; + json_bool c_boolean; +}; +struct json_object_double +{ + struct json_object base; + double c_double; +}; +struct json_object_int +{ + struct json_object base; + enum json_object_int_type cint_type; + union + { + int64_t c_int64; + uint64_t c_uint64; + } cint; +}; +struct json_object_string +{ + struct json_object base; + ssize_t len; // Signed b/c negative lengths indicate data is a pointer + // Consider adding an "alloc" field here, if json_object_set_string calls + // to expand the length of a string are common operations to perform. + union + { + char idata[1]; // Immediate data. Actually longer + char *pdata; // Only when len < 0 + } c_string; +}; + +void _json_c_set_last_err(const char *err_fmt, ...); + +extern const char *json_hex_chars; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_pointer.c b/comm/third_party/json-c/json_pointer.c new file mode 100644 index 0000000000..395567a5ba --- /dev/null +++ b/comm/third_party/json-c/json_pointer.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2016 Alexandru Ardelean. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include "strerror_override.h" + +#include +#include +#include +#include + +#include "json_pointer.h" +#include "strdup_compat.h" +#include "vasprintf_compat.h" + +/* Avoid ctype.h and locale overhead */ +#define is_plain_digit(c) ((c) >= '0' && (c) <= '9') + +/** + * JavaScript Object Notation (JSON) Pointer + * RFC 6901 - https://tools.ietf.org/html/rfc6901 + */ + +static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char) +{ + int slen = strlen(s); + int skip = strlen(occur) - 1; /* length of the occurrence, minus the char we're replacing */ + char *p = s; + while ((p = strstr(p, occur))) + { + *p = repl_char; + p++; + slen -= skip; + memmove(p, (p + skip), slen - (p - s) + 1); /* includes null char too */ + } +} + +static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx) +{ + int i, len = strlen(path); + /* this code-path optimizes a bit, for when we reference the 0-9 index range + * in a JSON array and because leading zeros not allowed + */ + if (len == 1) + { + if (is_plain_digit(path[0])) + { + *idx = (path[0] - '0'); + goto check_oob; + } + errno = EINVAL; + return 0; + } + /* leading zeros not allowed per RFC */ + if (path[0] == '0') + { + errno = EINVAL; + return 0; + } + /* RFC states base-10 decimals */ + for (i = 0; i < len; i++) + { + if (!is_plain_digit(path[i])) + { + errno = EINVAL; + return 0; + } + } + + *idx = strtol(path, NULL, 10); + if (*idx < 0) + { + errno = EINVAL; + return 0; + } +check_oob: + len = json_object_array_length(jo); + if (*idx >= len) + { + errno = ENOENT; + return 0; + } + + return 1; +} + +static int json_pointer_get_single_path(struct json_object *obj, char *path, + struct json_object **value) +{ + if (json_object_is_type(obj, json_type_array)) + { + int32_t idx; + if (!is_valid_index(obj, path, &idx)) + return -1; + obj = json_object_array_get_idx(obj, idx); + if (obj) + { + if (value) + *value = obj; + return 0; + } + /* Entry not found */ + errno = ENOENT; + return -1; + } + + /* RFC states that we first must eval all ~1 then all ~0 */ + string_replace_all_occurrences_with_char(path, "~1", '/'); + string_replace_all_occurrences_with_char(path, "~0", '~'); + + if (!json_object_object_get_ex(obj, path, value)) + { + errno = ENOENT; + return -1; + } + + return 0; +} + +static int json_pointer_set_single_path(struct json_object *parent, const char *path, + struct json_object *value) +{ + if (json_object_is_type(parent, json_type_array)) + { + int32_t idx; + /* RFC (Chapter 4) states that '-' may be used to add new elements to an array */ + if (path[0] == '-' && path[1] == '\0') + return json_object_array_add(parent, value); + if (!is_valid_index(parent, path, &idx)) + return -1; + return json_object_array_put_idx(parent, idx, value); + } + + /* path replacements should have been done in json_pointer_get_single_path(), + * and we should still be good here + */ + if (json_object_is_type(parent, json_type_object)) + return json_object_object_add(parent, path, value); + + /* Getting here means that we tried to "dereference" a primitive JSON type + * (like string, int, bool).i.e. add a sub-object to it + */ + errno = ENOENT; + return -1; +} + +static int json_pointer_get_recursive(struct json_object *obj, char *path, + struct json_object **value) +{ + char *endp; + int rc; + + /* All paths (on each recursion level must have a leading '/' */ + if (path[0] != '/') + { + errno = EINVAL; + return -1; + } + path++; + + endp = strchr(path, '/'); + if (endp) + *endp = '\0'; + + /* If we err-ed here, return here */ + if ((rc = json_pointer_get_single_path(obj, path, &obj))) + return rc; + + if (endp) + { + /* Put the slash back, so that the sanity check passes on next recursion level */ + *endp = '/'; + return json_pointer_get_recursive(obj, endp, value); + } + + /* We should be at the end of the recursion here */ + if (value) + *value = obj; + + return 0; +} + +int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res) +{ + char *path_copy = NULL; + int rc; + + if (!obj || !path) + { + errno = EINVAL; + return -1; + } + + if (path[0] == '\0') + { + if (res) + *res = obj; + return 0; + } + + /* pass a working copy to the recursive call */ + if (!(path_copy = strdup(path))) + { + errno = ENOMEM; + return -1; + } + rc = json_pointer_get_recursive(obj, path_copy, res); + free(path_copy); + + return rc; +} + +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...) +{ + char *path_copy = NULL; + int rc = 0; + va_list args; + + if (!obj || !path_fmt) + { + errno = EINVAL; + return -1; + } + + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') + { + if (res) + *res = obj; + goto out; + } + + rc = json_pointer_get_recursive(obj, path_copy, res); +out: + free(path_copy); + + return rc; +} + +int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value) +{ + const char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + int rc; + + if (!obj || !path) + { + errno = EINVAL; + return -1; + } + + if (path[0] == '\0') + { + json_object_put(*obj); + *obj = value; + return 0; + } + + if (path[0] != '/') + { + errno = EINVAL; + return -1; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path, '/')) == path) + { + path++; + return json_pointer_set_single_path(*obj, path, value); + } + + /* pass a working copy to the recursive call */ + if (!(path_copy = strdup(path))) + { + errno = ENOMEM; + return -1; + } + path_copy[endp - path] = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + free(path_copy); + + if (rc) + return rc; + + endp++; + return json_pointer_set_single_path(set, endp, value); +} + +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, + ...) +{ + char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + va_list args; + int rc = 0; + + if (!obj || !path_fmt) + { + errno = EINVAL; + return -1; + } + + /* pass a working copy to the recursive call */ + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') + { + json_object_put(*obj); + *obj = value; + goto out; + } + + if (path_copy[0] != '/') + { + errno = EINVAL; + rc = -1; + goto out; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path_copy, '/')) == path_copy) + { + set = *obj; + goto set_single_path; + } + + *endp = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + + if (rc) + goto out; + +set_single_path: + endp++; + rc = json_pointer_set_single_path(set, endp, value); +out: + free(path_copy); + return rc; +} diff --git a/comm/third_party/json-c/json_pointer.h b/comm/third_party/json-c/json_pointer.h new file mode 100644 index 0000000000..06c395b9ad --- /dev/null +++ b/comm/third_party/json-c/json_pointer.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2016 Alexadru Ardelean. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief JSON Pointer (RFC 6901) implementation for retrieving + * objects from a json-c object tree. + */ +#ifndef _json_pointer_h_ +#define _json_pointer_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Retrieves a JSON sub-object from inside another JSON object + * using the JSON pointer notation as defined in RFC 6901 + * https://tools.ietf.org/html/rfc6901 + * + * The returned JSON sub-object is equivalent to parsing manually the + * 'obj' JSON tree ; i.e. it's not a new object that is created, but rather + * a pointer inside the JSON tree. + * + * Internally, this is equivalent to doing a series of 'json_object_object_get()' + * and 'json_object_array_get_idx()' along the given 'path'. + * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'res' param will be treated as an argument for 'path' + * Example: json_pointer_get(obj, "/foo/%d/%s", &res, 0, bar) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * + * @param obj the json_object instance/tree from where to retrieve sub-objects + * @param path a (RFC6901) string notation for the sub-object to retrieve + * @param res a pointer that stores a reference to the json_object + * associated with the given path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +JSON_EXPORT int json_pointer_get(struct json_object *obj, const char *path, + struct json_object **res); + +/** + * This is a variant of 'json_pointer_get()' that supports printf() style arguments. + * + * Example: json_pointer_getf(obj, res, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param res a pointer that stores a reference to the json_object + * associated with the given path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +JSON_EXPORT int json_pointer_getf(struct json_object *obj, struct json_object **res, + const char *path_fmt, ...); + +/** + * Sets JSON object 'value' in the 'obj' tree at the location specified + * by the 'path'. 'path' is JSON pointer notation as defined in RFC 6901 + * https://tools.ietf.org/html/rfc6901 + * + * Note that 'obj' is a double pointer, mostly for the "" (empty string) + * case, where the entire JSON object would be replaced by 'value'. + * In the case of the "" path, the object at '*obj' will have it's refcount + * decremented with 'json_object_put()' and the 'value' object will be assigned to it. + * + * For other cases (JSON sub-objects) ownership of 'value' will be transferred into + * '*obj' via 'json_object_object_add()' & 'json_object_array_put_idx()', so the + * only time the refcount should be decremented for 'value' is when the return value of + * 'json_pointer_set()' is negative (meaning the 'value' object did not get set into '*obj'). + * + * That also implies that 'json_pointer_set()' does not do any refcount incrementing. + * (Just that single decrement that was mentioned above). + * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'value' param will be treated as an argument for 'path' + * Example: json_pointer_set(obj, "/foo/%d/%s", value, 0, bak) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param path a (RFC6901) string notation for the sub-object to set in the tree + * @param value object to set at path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +JSON_EXPORT int json_pointer_set(struct json_object **obj, const char *path, + struct json_object *value); + +/** + * This is a variant of 'json_pointer_set()' that supports printf() style arguments. + * + * Example: json_pointer_setf(obj, value, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param value object to set at path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +JSON_EXPORT int json_pointer_setf(struct json_object **obj, struct json_object *value, + const char *path_fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_tokener.c b/comm/third_party/json-c/json_tokener.c new file mode 100644 index 0000000000..0c09b66e8d --- /dev/null +++ b/comm/third_party/json-c/json_tokener.c @@ -0,0 +1,1300 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (https://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include "math_compat.h" +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_tokener.h" +#include "json_util.h" +#include "printbuf.h" +#include "strdup_compat.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ +#ifdef HAVE_XLOCALE_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif /* HAVE_STRINGS_H */ + +#define jt_hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x)&7) + 9) + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) +/* MSC has the version as _strnicmp */ +#define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +#error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +#if defined(_MSC_VER) && (_MSC_VER <= 1800) +/* VS2013 doesn't know about "inline" */ +#define inline __inline +#elif defined(AIX_CC) +#define inline +#endif + +/* The following helper functions are used to speed up parsing. They + * are faster than their ctype counterparts because they assume that + * the input is in ASCII and that the locale is set to "C". The + * compiler will also inline these functions, providing an additional + * speedup by saving on function calls. + */ +static inline int is_ws_char(char c) +{ + return c == ' ' + || c == '\t' + || c == '\n' + || c == '\r'; +} + +static inline int is_hex_char(char c) +{ + return (c >= '0' && c <= '9') + || (c >= 'A' && c <= 'F') + || (c >= 'a' && c <= 'f'); +} + +/* Use C99 NAN by default; if not available, nan("") should work too. */ +#ifndef NAN +#define NAN nan("") +#endif /* !NAN */ + +static const char json_null_str[] = "null"; +static const int json_null_str_len = sizeof(json_null_str) - 1; +static const char json_inf_str[] = "Infinity"; +/* Swapped case "Infinity" to avoid need to call tolower() on input chars: */ +static const char json_inf_str_invert[] = "iNFINITY"; +static const unsigned int json_inf_str_len = sizeof(json_inf_str) - 1; +static const char json_nan_str[] = "NaN"; +static const int json_nan_str_len = sizeof(json_nan_str) - 1; +static const char json_true_str[] = "true"; +static const int json_true_str_len = sizeof(json_true_str) - 1; +static const char json_false_str[] = "false"; +static const int json_false_str_len = sizeof(json_false_str) - 1; + +/* clang-format off */ +static const char *json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", + "invalid utf-8 string", + "buffer size overflow" +}; +/* clang-format on */ + +/** + * validete the utf-8 string in strict model. + * if not utf-8 format, return err. + */ +static json_bool json_tokener_validate_utf8(const char c, unsigned int *nBytes); + +static int json_tokener_parse_double(const char *buf, int len, double *retval); + +const char *json_tokener_error_desc(enum json_tokener_error jerr) +{ + int jerr_int = (int)jerr; + if (jerr_int < 0 || + jerr_int >= (int)(sizeof(json_tokener_errors) / sizeof(json_tokener_errors[0]))) + return "Unknown error, " + "invalid json_tokener_error value passed to json_tokener_error_desc()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc)&0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc)&0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi, lo) ((((hi)&0x3FF) << 10) + ((lo)&0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = {0xEF, 0xBF, 0xBD}; + +struct json_tokener *json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + tok = (struct json_tokener *)calloc(1, sizeof(struct json_tokener)); + if (!tok) + return NULL; + tok->stack = (struct json_tokener_srec *)calloc(depth, sizeof(struct json_tokener_srec)); + if (!tok->stack) + { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + if (!tok->pb) + { + free(tok->stack); + free(tok); + return NULL; + } + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener *json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if (tok->pb) + printbuf_free(tok->pb); + free(tok->stack); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for (i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object *json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object *obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object *json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +{ + struct json_tokener *tok; + struct json_object *obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if (tok->err != json_tokener_success +#if 0 + /* This would be a more sensible default, and cause parsing + * things like "null123" to fail when the caller can't know + * where the parsing left off, but starting to fail would + * be a notable behaviour change. Save for a 1.0 release. + */ + || json_tokener_get_parse_end(tok) != strlen(str) +#endif + ) + + { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * Returns 1 on success, sets tok->err and returns 0 if no more chars. + * Implicit inputs: str, len, nBytesp vars + */ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) \ + ? (((tok)->depth == 0 && state == json_tokener_state_eatws && \ + saved_state == json_tokener_state_finish) \ + ? (((tok)->err = json_tokener_success), 0) \ + : (((tok)->err = json_tokener_continue), 0)) \ + : (((tok->flags & JSON_TOKENER_VALIDATE_UTF8) && \ + (!json_tokener_validate_utf8(*str, nBytesp))) \ + ? ((tok->err = json_tokener_error_parse_utf8_string), 0) \ + : (((dest) = *str), 1))) + +/* ADVANCE_CHAR() macro: + * Increments str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) (++(str), ((tok)->char_offset)++, c) + +/* End optimization macro defs */ + +struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *str, int len) +{ + struct json_object *obj = NULL; + char c = '\1'; + unsigned int nBytes = 0; + unsigned int *nBytesp = &nBytes; + +#ifdef HAVE_USELOCALE + locale_t oldlocale = uselocale(NULL); + locale_t newloc; +#elif defined(HAVE_SETLOCALE) + char *oldlocale = NULL; +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + /* this interface is presently not 64-bit clean due to the int len argument + * and the internal printbuf interface that takes 32-bit int len arguments + * so the function limits the maximum string size to INT32_MAX (2GB). + * If the function is called with len == -1 then strlen is called to check + * the string length is less than INT32_MAX (2GB) + */ + if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) + { + tok->err = json_tokener_error_size; + return NULL; + } + +#ifdef HAVE_USELOCALE + { + locale_t duploc = duplocale(oldlocale); + newloc = newlocale(LC_NUMERIC_MASK, "C", duploc); + if (newloc == NULL) + { + freelocale(duploc); + return NULL; + } + uselocale(newloc); + } +#elif defined(HAVE_SETLOCALE) + { + char *tmplocale; + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) + oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); + } +#endif + + while (PEEK_CHAR(c, tok)) // Note: c might be '\0' ! + { + + redo_char: + switch (state) + { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (is_ws_char(c)) + { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if (c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) + { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } + else + { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch (c) + { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + if (current == NULL) + goto out; + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + if (current == NULL) + goto out; + break; + case 'I': + case 'i': + state = json_tokener_state_inf; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case 'N': + case 'n': + state = json_tokener_state_null; // or NaN + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '\'': + if (tok->flags & JSON_TOKENER_STRICT) + { + /* in STRICT mode only double-quote are allowed */ + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + /* FALLTHRU */ + case '"': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: tok->err = json_tokener_error_parse_unexpected; goto out; + } + break; + + case json_tokener_state_finish: + if (tok->depth == 0) + goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_inf: /* aka starts with 'i' (or 'I', or "-i", or "-I") */ + { + /* If we were guaranteed to have len set, then we could (usually) handle + * the entire "Infinity" check in a single strncmp (strncasecmp), but + * since len might be -1 (i.e. "read until \0"), we need to check it + * a character at a time. + * Trying to handle it both ways would make this code considerably more + * complicated with likely little performance benefit. + */ + int is_negative = 0; + + /* Note: tok->st_pos must be 0 when state is set to json_tokener_state_inf */ + while (tok->st_pos < (int)json_inf_str_len) + { + char inf_char = *str; + if (inf_char != json_inf_str[tok->st_pos] && + ((tok->flags & JSON_TOKENER_STRICT) || + inf_char != json_inf_str_invert[tok->st_pos]) + ) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + tok->st_pos++; + (void)ADVANCE_CHAR(str, tok); + if (!PEEK_CHAR(c, tok)) + { + /* out of input chars, for now at least */ + goto out; + } + } + /* We checked the full length of "Infinity", so create the object. + * When handling -Infinity, the number parsing code will have dropped + * the "-" into tok->pb for us, so check it now. + */ + if (printbuf_length(tok->pb) > 0 && *(tok->pb->buf) == '-') + { + is_negative = 1; + } + current = json_object_new_double(is_negative ? -INFINITY : INFINITY); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + case json_tokener_state_null: /* aka starts with 'n' */ + { + int size; + int size_nan; + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos + 1, json_null_str_len); + size_nan = json_min(tok->st_pos + 1, json_nan_str_len); + if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_null_str, tok->pb->buf, size) == 0) || + (strncmp(json_null_str, tok->pb->buf, size) == 0)) + { + if (tok->st_pos == json_null_str_len) + { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_nan_str, tok->pb->buf, size_nan) == 0) || + (strncmp(json_nan_str, tok->pb->buf, size_nan) == 0)) + { + if (tok->st_pos == json_nan_str_len) + { + current = json_object_new_double(NAN); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else + { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_comment_start: + if (c == '*') + { + state = json_tokener_state_comment; + } + else if (c == '/') + { + state = json_tokener_state_comment_eol; + } + else + { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while (c != '*') + { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1 + str - case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while (c != '\n') + { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str - case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if (c == '/') + { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + else + { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + while (1) + { + if (c == tok->quote_char) + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + current = + json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } + else if (c == '\\') + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch (c) + { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if (c == 'b') + printbuf_memappend_fast(tok->pb, "\b", 1); + else if (c == 'n') + printbuf_memappend_fast(tok->pb, "\n", 1); + else if (c == 'r') + printbuf_memappend_fast(tok->pb, "\r", 1); + else if (c == 't') + printbuf_memappend_fast(tok->pb, "\t", 1); + else if (c == 'f') + printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: tok->err = json_tokener_error_parse_string; goto out; + } + break; + + // =================================================== + + case json_tokener_state_escape_unicode: + { + /* Handle a 4-byte \uNNNN sequence, or two sequences if a surrogate pair */ + while (1) + { + if (!c || !is_hex_char(c)) + { + tok->err = json_tokener_error_parse_string; + goto out; + } + tok->ucs_char |= + ((unsigned int)jt_hexdigit(c) << ((3 - tok->st_pos) * 4)); + tok->st_pos++; + if (tok->st_pos >= 4) + break; + + (void)ADVANCE_CHAR(str, tok); + if (!PEEK_CHAR(c, tok)) + { + /* + * We're out of characters in the current call to + * json_tokener_parse(), but a subsequent call might + * provide us with more, so leave our current state + * as-is (including tok->high_surrogate) and return. + */ + goto out; + } + } + tok->st_pos = 0; + + /* Now, we have a full \uNNNN sequence in tok->ucs_char */ + + /* If the *previous* sequence was a high surrogate ... */ + if (tok->high_surrogate) + { + if (IS_LOW_SURROGATE(tok->ucs_char)) + { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(tok->high_surrogate, + tok->ucs_char); + } + else + { + /* High surrogate was not followed by a low surrogate + * Replace the high and process the rest normally + */ + printbuf_memappend_fast(tok->pb, + (char *)utf8_replacement_char, 3); + } + tok->high_surrogate = 0; + } + + if (tok->ucs_char < 0x80) + { + unsigned char unescaped_utf[1]; + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 1); + } + else if (tok->ucs_char < 0x800) + { + unsigned char unescaped_utf[2]; + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 2); + } + else if (IS_HIGH_SURROGATE(tok->ucs_char)) + { + /* + * The next two characters should be \u, HOWEVER, + * we can't simply peek ahead here, because the + * characters we need might not be passed to us + * until a subsequent call to json_tokener_parse. + * Instead, transition through a couple of states. + * (now): + * _escape_unicode => _unicode_need_escape + * (see a '\\' char): + * _unicode_need_escape => _unicode_need_u + * (see a 'u' char): + * _unicode_need_u => _escape_unicode + * ...and we'll end up back around here. + */ + tok->high_surrogate = tok->ucs_char; + tok->ucs_char = 0; + state = json_tokener_state_escape_unicode_need_escape; + break; + } + else if (IS_LOW_SURROGATE(tok->ucs_char)) + { + /* Got a low surrogate not preceded by a high */ + printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); + } + else if (tok->ucs_char < 0x10000) + { + unsigned char unescaped_utf[3]; + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 3); + } + else if (tok->ucs_char < 0x110000) + { + unsigned char unescaped_utf[4]; + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 4); + } + else + { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); + } + state = saved_state; // i.e. _state_string or _state_object_field + } + break; + + case json_tokener_state_escape_unicode_need_escape: + // We get here after processing a high_surrogate + // require a '\\' char + if (!c || c != '\\') + { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the high surrogate + * and pop back up to _state_string or _state_object_field. + */ + printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); + tok->high_surrogate = 0; + tok->ucs_char = 0; + tok->st_pos = 0; + state = saved_state; + goto redo_char; + } + state = json_tokener_state_escape_unicode_need_u; + break; + + case json_tokener_state_escape_unicode_need_u: + /* We already had a \ char, check that it's \u */ + if (!c || c != 'u') + { + /* Got a high surrogate with some non-unicode escape + * sequence following it. + * Put a replacement char in for the high surrogate + * and handle the escape sequence normally. + */ + printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); + tok->high_surrogate = 0; + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_string_escape; + goto redo_char; + } + state = json_tokener_state_escape_unicode; + break; + + // =================================================== + + case json_tokener_state_boolean: + { + int size1, size2; + printbuf_memappend_fast(tok->pb, &c, 1); + size1 = json_min(tok->st_pos + 1, json_true_str_len); + size2 = json_min(tok->st_pos + 1, json_false_str_len); + if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_true_str, tok->pb->buf, size1) == 0) || + (strncmp(json_true_str, tok->pb->buf, size1) == 0)) + { + if (tok->st_pos == json_true_str_len) + { + current = json_object_new_boolean(1); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_false_str, tok->pb->buf, size2) == 0) || + (strncmp(json_false_str, tok->pb->buf, size2) == 0)) + { + if (tok->st_pos == json_false_str_len) + { + current = json_object_new_boolean(0); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else + { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len = 0; + int is_exponent = 0; + int neg_sign_ok = 1; + int pos_sign_ok = 0; + if (printbuf_length(tok->pb) > 0) + { + /* We don't save all state from the previous incremental parse + so we need to re-generate it based on the saved string so far. + */ + char *e_loc = strchr(tok->pb->buf, 'e'); + if (!e_loc) + e_loc = strchr(tok->pb->buf, 'E'); + if (e_loc) + { + char *last_saved_char = + &tok->pb->buf[printbuf_length(tok->pb) - 1]; + is_exponent = 1; + pos_sign_ok = neg_sign_ok = 1; + /* If the "e" isn't at the end, we can't start with a '-' */ + if (e_loc != last_saved_char) + { + neg_sign_ok = 0; + pos_sign_ok = 0; + } + // else leave it set to 1, i.e. start of the new input + } + } + + while (c && ((c >= '0' && c <= '9') || + (!is_exponent && (c == 'e' || c == 'E')) || + (neg_sign_ok && c == '-') || (pos_sign_ok && c == '+') || + (!tok->is_double && c == '.'))) + { + pos_sign_ok = neg_sign_ok = 0; + ++case_len; + + /* non-digit characters checks */ + /* note: since the main loop condition to get here was + * an input starting with 0-9 or '-', we are + * protected from input starting with '.' or + * e/E. + */ + switch (c) + { + case '.': + tok->is_double = 1; + pos_sign_ok = 1; + neg_sign_ok = 1; + break; + case 'e': /* FALLTHRU */ + case 'E': + is_exponent = 1; + tok->is_double = 1; + /* the exponent part can begin with a negative sign */ + pos_sign_ok = neg_sign_ok = 1; + break; + default: break; + } + + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) + { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + /* + Now we know c isn't a valid number char, but check whether + it might have been intended to be, and return a potentially + more understandable error right away. + However, if we're at the top-level, use the number as-is + because c can be part of a new object to parse on the + next call to json_tokener_parse(). + */ + if (tok->depth > 0 && c != ',' && c != ']' && c != '}' && c != '/' && + c != 'I' && c != 'i' && !is_ws_char(c)) + { + tok->err = json_tokener_error_parse_number; + goto out; + } + if (case_len > 0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + + // Check for -Infinity + if (tok->pb->buf[0] == '-' && case_len <= 1 && (c == 'i' || c == 'I')) + { + state = json_tokener_state_inf; + tok->st_pos = 0; + goto redo_char; + } + if (tok->is_double && !(tok->flags & JSON_TOKENER_STRICT)) + { + /* Trim some chars off the end, to allow things + like "123e+" to parse ok. */ + while (printbuf_length(tok->pb) > 1) + { + char last_char = tok->pb->buf[printbuf_length(tok->pb) - 1]; + if (last_char != 'e' && last_char != 'E' && + last_char != '-' && last_char != '+') + { + break; + } + tok->pb->buf[printbuf_length(tok->pb) - 1] = '\0'; + printbuf_length(tok->pb)--; + } + } + } + { + int64_t num64; + uint64_t numuint64; + double numd; + if (!tok->is_double && tok->pb->buf[0] == '-' && + json_parse_int64(tok->pb->buf, &num64) == 0) + { + current = json_object_new_int64(num64); + if (current == NULL) + goto out; + } + else if (!tok->is_double && tok->pb->buf[0] != '-' && + json_parse_uint64(tok->pb->buf, &numuint64) == 0) + { + if (numuint64 && tok->pb->buf[0] == '0' && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_number; + goto out; + } + if (numuint64 <= INT64_MAX) + { + num64 = (uint64_t)numuint64; + current = json_object_new_int64(num64); + if (current == NULL) + goto out; + } + else + { + current = json_object_new_uint64(numuint64); + if (current == NULL) + goto out; + } + } + else if (tok->is_double && + json_tokener_parse_double( + tok->pb->buf, printbuf_length(tok->pb), &numd) == 0) + { + current = json_object_new_double_s(numd, tok->pb->buf); + if (current == NULL) + goto out; + } + else + { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array_after_sep: + case json_tokener_state_array: + if (c == ']') + { + // Minimize memory usage; assume parsed objs are unlikely to be changed + json_object_array_shrink(current, 0); + + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } + else + { + if (tok->depth >= tok->max_depth - 1) + { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + if (json_object_array_add(current, obj) != 0) + goto out; + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if (c == ']') + { + // Minimize memory usage; assume parsed objs are unlikely to be changed + json_object_array_shrink(current, 0); + + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } + else if (c == ',') + { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } + else + { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if (c == '}') + { + if (state == json_tokener_state_object_field_start_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } + else if (c == '"' || c == '\'') + { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } + else + { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while (1) + { + if (c == tok->quote_char) + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } + else if (c == '\\') + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) + { + printbuf_memappend_fast(tok->pb, case_start, + str - case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if (c == ':') + { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } + else + { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if (tok->depth >= tok->max_depth - 1) + { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + /* { */ + if (c == '}') + { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } + else if (c == ',') + { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } + else + { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + } + (void)ADVANCE_CHAR(str, tok); + if (!c) // This is the char *before* advancing + break; + } /* while(PEEK_CHAR) */ + +out: + if ((tok->flags & JSON_TOKENER_VALIDATE_UTF8) && (nBytes != 0)) + { + tok->err = json_tokener_error_parse_utf8_string; + } + if (c && (state == json_tokener_state_finish) && (tok->depth == 0) && + (tok->flags & (JSON_TOKENER_STRICT | JSON_TOKENER_ALLOW_TRAILING_CHARS)) == + JSON_TOKENER_STRICT) + { + /* unexpected char after JSON data */ + tok->err = json_tokener_error_parse_unexpected; + } + if (!c) + { + /* We hit an eof char (0) */ + if (state != json_tokener_state_finish && saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_USELOCALE + uselocale(oldlocale); + freelocale(newloc); +#elif defined(HAVE_SETLOCALE) + setlocale(LC_NUMERIC, oldlocale); + free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for (ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", json_tokener_errors[tok->err], + tok->char_offset); + return NULL; +} + +static json_bool json_tokener_validate_utf8(const char c, unsigned int *nBytes) +{ + unsigned char chr = c; + if (*nBytes == 0) + { + if (chr >= 0x80) + { + if ((chr & 0xe0) == 0xc0) + *nBytes = 1; + else if ((chr & 0xf0) == 0xe0) + *nBytes = 2; + else if ((chr & 0xf8) == 0xf0) + *nBytes = 3; + else + return 0; + } + } + else + { + if ((chr & 0xC0) != 0x80) + return 0; + (*nBytes)--; + } + return 1; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} + +size_t json_tokener_get_parse_end(struct json_tokener *tok) +{ + assert(tok->char_offset >= 0); /* Drop this line when char_offset becomes a size_t */ + return (size_t)tok->char_offset; +} + +static int json_tokener_parse_double(const char *buf, int len, double *retval) +{ + char *end; + *retval = strtod(buf, &end); + if (buf + len == end) + return 0; // It worked + return 1; +} diff --git a/comm/third_party/json-c/json_tokener.h b/comm/third_party/json-c/json_tokener.h new file mode 100644 index 0000000000..a07e12ce7e --- /dev/null +++ b/comm/third_party/json-c/json_tokener.h @@ -0,0 +1,328 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Methods to parse an input string into a tree of json_object objects. + */ +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include "json_object.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error +{ + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment, + json_tokener_error_parse_utf8_string, + json_tokener_error_size +}; + +/** + * @deprecated Don't use this outside of json_tokener.c, it will be made private in a future release. + */ +enum json_tokener_state +{ + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_escape_unicode_need_escape, + json_tokener_state_escape_unicode_need_u, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep, + json_tokener_state_inf +}; + +/** + * @deprecated Don't use this outside of json_tokener.c, it will be made private in a future release. + */ +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +/** + * Internal state of the json parser. + * Do not access any fields of this structure directly. + * Its definition is published due to historical limitations + * in the json tokener API, and will be changed to be an opaque + * type in the future. + */ +struct json_tokener +{ + /** + * @deprecated Do not access any of these fields outside of json_tokener.c + */ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos; + /** + * @deprecated See json_tokener_get_parse_end() instead. + */ + int char_offset; + /** + * @deprecated See json_tokener_get_error() instead. + */ + enum json_tokener_error err; + unsigned int ucs_char, high_surrogate; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; + +/** + * Return the offset of the byte after the last byte parsed + * relative to the start of the most recent string passed in + * to json_tokener_parse_ex(). i.e. this is where parsing + * would start again if the input contains another JSON object + * after the currently parsed one. + * + * Note that when multiple parse calls are issued, this is *not* the + * total number of characters parsed. + * + * In the past this would have been accessed as tok->char_offset. + * + * See json_tokener_parse_ex() for an example of how to use this. + */ +JSON_EXPORT size_t json_tokener_get_parse_end(struct json_tokener *tok); + +/** + * @deprecated Unused in json-c code + */ +typedef struct json_tokener json_tokener; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * Note that setting this will also effectively disable parsing + * of multiple json objects in a single character stream + * (e.g. {"foo":123}{"bar":234}); if you want to allow that + * also set JSON_TOKENER_ALLOW_TRAILING_CHARS + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Use with JSON_TOKENER_STRICT to allow trailing characters after the + * first parsed object. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_ALLOW_TRAILING_CHARS 0x02 + +/** + * Cause json_tokener_parse_ex() to validate that input is UTF8. + * If this flag is specified and validation fails, then + * json_tokener_get_error(tok) will return + * json_tokener_error_parse_utf8_string + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_VALIDATE_UTF8 0x10 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +JSON_EXPORT const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * @see json_tokener_error_desc(). + */ +JSON_EXPORT enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); + +/** + * Allocate a new json_tokener. + * When done using that to parse objects, free it with json_tokener_free(). + * See json_tokener_parse_ex() for usage details. + */ +JSON_EXPORT struct json_tokener *json_tokener_new(void); + +/** + * Allocate a new json_tokener with a custom max nesting depth. + * @see JSON_TOKENER_DEFAULT_DEPTH + */ +JSON_EXPORT struct json_tokener *json_tokener_new_ex(int depth); + +/** + * Free a json_tokener previously allocated with json_tokener_new(). + */ +JSON_EXPORT void json_tokener_free(struct json_tokener *tok); + +/** + * Reset the state of a json_tokener, to prepare to parse a + * brand new JSON object. + */ +JSON_EXPORT void json_tokener_reset(struct json_tokener *tok); + +/** + * Parse a json_object out of the string `str`. + * + * If you need more control over how the parsing occurs, + * see json_tokener_parse_ex(). + */ +JSON_EXPORT struct json_object *json_tokener_parse(const char *str); + +/** + * Parser a json_object out of the string `str`, but if it fails + * return the error in `*error`. + * @see json_tokener_parse() + * @see json_tokener_parse_ex() + */ +JSON_EXPORT struct json_object *json_tokener_parse_verbose(const char *str, + enum json_tokener_error *error); + +/** + * Set flags that control how parsing will be done. + */ +JSON_EXPORT void json_tokener_set_flags(struct json_tokener *tok, int flags); + +/** + * Parse a string and return a non-NULL json_object if a valid JSON value + * is found. The string does not need to be a JSON object or array; + * it can also be a string, number or boolean value. + * + * A partial JSON string can be parsed. If the parsing is incomplete, + * NULL will be returned and json_tokener_get_error() will return + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error is anything other than + * json_tokener_continue, a fatal error has occurred and parsing must be + * halted. Then, the tok object must not be reused until json_tokener_reset() + * is called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned, with a reference count of one which belongs to the caller. Also, + * json_tokener_get_error() will return json_tokener_success. Be sure to check + * the type with json_object_is_type() or json_object_get_type() before using + * the object. + * + * Trailing characters after the parsed value do not automatically cause an + * error. It is up to the caller to decide whether to treat this as an + * error or to handle the additional characters, perhaps by parsing another + * json value starting from that point. + * + * If the caller knows that they are at the end of their input, the length + * passed MUST include the final '\0' character, so values with no inherent + * end (i.e. numbers) can be properly parsed, rather than just returning + * json_tokener_continue. + * + * Extra characters can be detected by comparing the value returned by + * json_tokener_get_parse_end() against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for a subsequent call to json_tokener_parse_ex with an + * appropriate str parameter starting with the extra characters. + * + * This interface is presently not 64-bit clean due to the int len argument + * so the function limits the maximum string size to INT32_MAX (2GB). + * If the function is called with len == -1 then strlen is called to check + * the string length is less than INT32_MAX (2GB) + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + if (end_of_input) + stringlen++; // Include the '\0' if we know we're at the end of input + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (json_tokener_get_parse_end(tok) < stringlen) +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. + * @param len the length of str + */ +JSON_EXPORT struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *str, + int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_types.h b/comm/third_party/json-c/json_types.h new file mode 100644 index 0000000000..b7e55ada89 --- /dev/null +++ b/comm/third_party/json-c/json_types.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Eric Hawicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#ifndef _json_types_h_ +#define _json_types_h_ + +/** + * @file + * @brief Basic types used in a few places in json-c, but you should include "json_object.h" instead. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JSON_EXPORT +#if defined(_MSC_VER) && defined(JSON_C_DLL) +#define JSON_EXPORT __declspec(dllexport) +#else +#define JSON_EXPORT extern +#endif +#endif + +struct printbuf; + +/** + * A structure to use with json_object_object_foreachC() loops. + * Contains key, val and entry members. + */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; +typedef struct json_object_iter json_object_iter; + +typedef int json_bool; + +/** + * @brief The core type for all type of JSON objects handled by json-c + */ +typedef struct json_object json_object; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void(json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int(json_object_to_json_string_fn)(struct json_object *jso, struct printbuf *pb, int level, + int flags); + +/* supported object types */ + +typedef enum json_type +{ + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string +} json_type; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_util.c b/comm/third_party/json-c/json_util.c new file mode 100644 index 0000000000..3e6a6c681b --- /dev/null +++ b/comm/third_party/json-c/json_util.c @@ -0,0 +1,296 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" +#undef realloc + +#include "strerror_override.h" + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif /* defined(WIN32) */ + +#if !defined(HAVE_OPEN) && defined(WIN32) +#define open _open +#endif + +#include "snprintf_compat.h" + +#include "debug.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" +#include "printbuf.h" + +static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename); + +static char _last_err[256] = ""; + +const char *json_util_get_last_err() +{ + if (_last_err[0] == '\0') + return NULL; + return _last_err; +} + +void _json_c_set_last_err(const char *err_fmt, ...) +{ + va_list ap; + va_start(ap, err_fmt); + // Ignore (attempted) overruns from snprintf + (void)vsnprintf(_last_err, sizeof(_last_err), err_fmt, ap); + va_end(ap); +} + +struct json_object *json_object_from_fd(int fd) +{ + return json_object_from_fd_ex(fd, -1); +} +struct json_object *json_object_from_fd_ex(int fd, int in_depth) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int ret; + int depth = JSON_TOKENER_DEFAULT_DEPTH; + json_tokener *tok; + + if (!(pb = printbuf_new())) + { + _json_c_set_last_err("json_object_from_fd_ex: printbuf_new failed\n"); + return NULL; + } + + if (in_depth != -1) + depth = in_depth; + tok = json_tokener_new_ex(depth); + if (!tok) + { + _json_c_set_last_err( + "json_object_from_fd_ex: unable to allocate json_tokener(depth=%d): %s\n", depth, + strerror(errno)); + printbuf_free(pb); + return NULL; + } + + while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) + { + printbuf_memappend(pb, buf, ret); + } + if (ret < 0) + { + _json_c_set_last_err("json_object_from_fd_ex: error reading fd %d: %s\n", fd, + strerror(errno)); + json_tokener_free(tok); + printbuf_free(pb); + return NULL; + } + + obj = json_tokener_parse_ex(tok, pb->buf, printbuf_length(pb)); + if (obj == NULL) + _json_c_set_last_err("json_tokener_parse_ex failed: %s\n", + json_tokener_error_desc(json_tokener_get_error(tok))); + + json_tokener_free(tok); + printbuf_free(pb); + return obj; +} + +struct json_object *json_object_from_file(const char *filename) +{ + struct json_object *obj; + int fd; + + if ((fd = open(filename, O_RDONLY)) < 0) + { + _json_c_set_last_err("json_object_from_file: error opening file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + obj = json_object_from_fd(fd); + close(fd); + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) +{ + int fd, ret; + int saved_errno; + + if (!obj) + { + _json_c_set_last_err("json_object_to_file_ext: object is null\n"); + return -1; + } + + if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) + { + _json_c_set_last_err("json_object_to_file_ext: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + ret = _json_object_to_fd(fd, obj, flags, filename); + saved_errno = errno; + close(fd); + errno = saved_errno; + return ret; +} + +int json_object_to_fd(int fd, struct json_object *obj, int flags) +{ + if (!obj) + { + _json_c_set_last_err("json_object_to_fd: object is null\n"); + return -1; + } + + return _json_object_to_fd(fd, obj, flags, NULL); +} +static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename) +{ + int ret; + const char *json_str; + unsigned int wpos, wsize; + + filename = filename ? filename : "(fd)"; + + if (!(json_str = json_object_to_json_string_ext(obj, flags))) + { + return -1; + } + + /* CAW: probably unnecessary, but the most 64bit safe */ + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); + wpos = 0; + while (wpos < wsize) + { + if ((ret = write(fd, json_str + wpos, wsize - wpos)) < 0) + { + _json_c_set_last_err("json_object_to_fd: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(const char *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +// Deprecated json_parse_double function. See json_tokener_parse_double instead. +int json_parse_double(const char *buf, double *retval) +{ + char *end; + *retval = strtod(buf, &end); + return end == buf ? 1 : 0; +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + char *end = NULL; + int64_t val; + + errno = 0; + val = strtoll(buf, &end, 10); + if (end != buf) + *retval = val; + return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; +} + +int json_parse_uint64(const char *buf, uint64_t *retval) +{ + char *end = NULL; + uint64_t val; + + errno = 0; + while (*buf == ' ') + buf++; + if (*buf == '-') + return 1; /* error: uint cannot be negative */ + + val = strtoull(buf, &end, 10); + if (end != buf) + *retval = val; + return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; +} + +#ifndef HAVE_REALLOC +void *rpl_realloc(void *p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +/* clang-format off */ +static const char *json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; +/* clang-format on */ + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + _json_c_set_last_err("json_type_to_name: type %d is out of range [0,%u]\n", o_type, + (unsigned)NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} diff --git a/comm/third_party/json-c/json_util.h b/comm/third_party/json-c/json_util.h new file mode 100644 index 0000000000..1f663e872a --- /dev/null +++ b/comm/third_party/json-c/json_util.h @@ -0,0 +1,121 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Miscllaneous utility functions and macros. + */ +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifndef json_min +#define json_min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +/** + * Read the full contents of the given file, then convert it to a + * json_object using json_tokener_parse(). + * + * Returns NULL on failure. See json_util_get_last_err() for details. + */ +JSON_EXPORT struct json_object *json_object_from_file(const char *filename); + +/** + * Create a JSON object from already opened file descriptor. + * + * This function can be helpful, when you opened the file already, + * e.g. when you have a temp file. + * Note, that the fd must be readable at the actual position, i.e. + * use lseek(fd, 0, SEEK_SET) before. + * + * The depth argument specifies the maximum object depth to pass to + * json_tokener_new_ex(). When depth == -1, JSON_TOKENER_DEFAULT_DEPTH + * is used instead. + * + * Returns NULL on failure. See json_util_get_last_err() for details. + */ +JSON_EXPORT struct json_object *json_object_from_fd_ex(int fd, int depth); + +/** + * Create a JSON object from an already opened file descriptor, using + * the default maximum object depth. (JSON_TOKENER_DEFAULT_DEPTH) + * + * See json_object_from_fd_ex() for details. + */ +JSON_EXPORT struct json_object *json_object_from_fd(int fd); + +/** + * Equivalent to: + * json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +JSON_EXPORT int json_object_to_file(const char *filename, struct json_object *obj); + +/** + * Open and truncate the given file, creating it if necessary, then + * convert the json_object to a string and write it to the file. + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +JSON_EXPORT int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags); + +/** + * Convert the json_object to a string and write it to the file descriptor. + * Handles partial writes and will keep writing until done, or an error + * occurs. + * + * @param fd an open, writable file descriptor to write to + * @param obj the object to serializer and write + * @param flags flags to pass to json_object_to_json_string_ext() + * @return -1 if something fails. See json_util_get_last_err() for details. + */ +JSON_EXPORT int json_object_to_fd(int fd, struct json_object *obj, int flags); + +/** + * Return the last error from various json-c functions, including: + * json_object_to_file{,_ext}, json_object_to_fd() or + * json_object_from_{file,fd}, or NULL if there is none. + */ +JSON_EXPORT const char *json_util_get_last_err(void); + +/* these parsing helpers return zero on success */ +JSON_EXPORT int json_parse_int64(const char *buf, int64_t *retval); +JSON_EXPORT int json_parse_uint64(const char *buf, uint64_t *retval); +/** + * @deprecated + */ +JSON_EXPORT int json_parse_double(const char *buf, double *retval); + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +JSON_EXPORT const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/json_visit.c b/comm/third_party/json-c/json_visit.c new file mode 100644 index 0000000000..fb16fa6fa6 --- /dev/null +++ b/comm/third_party/json-c/json_visit.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 Eric Haszlakiewicz + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#include + +#include "config.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_visit.h" +#include "linkhash.h" + +static int _json_c_visit(json_object *jso, json_object *parent_jso, const char *jso_key, + size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg); + +int json_c_visit(json_object *jso, int future_flags, json_c_visit_userfunc *userfunc, void *userarg) +{ + int ret = _json_c_visit(jso, NULL, NULL, NULL, userfunc, userarg); + switch (ret) + { + case JSON_C_VISIT_RETURN_CONTINUE: + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + case JSON_C_VISIT_RETURN_STOP: return 0; + default: return JSON_C_VISIT_RETURN_ERROR; + } +} +static int _json_c_visit(json_object *jso, json_object *parent_jso, const char *jso_key, + size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg) +{ + int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); + switch (userret) + { + case JSON_C_VISIT_RETURN_CONTINUE: break; + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + case JSON_C_VISIT_RETURN_STOP: + case JSON_C_VISIT_RETURN_ERROR: return userret; + default: + fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", + userret); + return JSON_C_VISIT_RETURN_ERROR; + } + + switch (json_object_get_type(jso)) + { + case json_type_null: + case json_type_boolean: + case json_type_double: + case json_type_int: + case json_type_string: + // we already called userfunc above, move on to the next object + return JSON_C_VISIT_RETURN_CONTINUE; + + case json_type_object: + { + json_object_object_foreach(jso, key, child) + { + userret = _json_c_visit(child, jso, key, NULL, userfunc, userarg); + if (userret == JSON_C_VISIT_RETURN_POP) + break; + if (userret == JSON_C_VISIT_RETURN_STOP || + userret == JSON_C_VISIT_RETURN_ERROR) + return userret; + if (userret != JSON_C_VISIT_RETURN_CONTINUE && + userret != JSON_C_VISIT_RETURN_SKIP) + { + fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", + userret); + return JSON_C_VISIT_RETURN_ERROR; + } + } + break; + } + case json_type_array: + { + size_t array_len = json_object_array_length(jso); + size_t ii; + for (ii = 0; ii < array_len; ii++) + { + json_object *child = json_object_array_get_idx(jso, ii); + userret = _json_c_visit(child, jso, NULL, &ii, userfunc, userarg); + if (userret == JSON_C_VISIT_RETURN_POP) + break; + if (userret == JSON_C_VISIT_RETURN_STOP || + userret == JSON_C_VISIT_RETURN_ERROR) + return userret; + if (userret != JSON_C_VISIT_RETURN_CONTINUE && + userret != JSON_C_VISIT_RETURN_SKIP) + { + fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", + userret); + return JSON_C_VISIT_RETURN_ERROR; + } + } + break; + } + default: + fprintf(stderr, "INTERNAL ERROR: _json_c_visit found object of unknown type: %d\n", + json_object_get_type(jso)); + return JSON_C_VISIT_RETURN_ERROR; + } + + // Call userfunc for the second type on container types, after all + // members of the container have been visited. + // Non-container types will have already returned before this point. + + userret = userfunc(jso, JSON_C_VISIT_SECOND, parent_jso, jso_key, jso_index, userarg); + switch (userret) + { + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + // These are not really sensible during JSON_C_VISIT_SECOND, + // but map them to JSON_C_VISIT_CONTINUE anyway. + // FALLTHROUGH + case JSON_C_VISIT_RETURN_CONTINUE: return JSON_C_VISIT_RETURN_CONTINUE; + case JSON_C_VISIT_RETURN_STOP: + case JSON_C_VISIT_RETURN_ERROR: return userret; + default: + fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", + userret); + return JSON_C_VISIT_RETURN_ERROR; + } + // NOTREACHED +} diff --git a/comm/third_party/json-c/json_visit.h b/comm/third_party/json-c/json_visit.h new file mode 100644 index 0000000000..35c46f5b18 --- /dev/null +++ b/comm/third_party/json-c/json_visit.h @@ -0,0 +1,101 @@ + +#ifndef _json_c_json_visit_h_ +#define _json_c_json_visit_h_ + +/** + * @file + * @brief Methods for walking a tree of objects. + */ +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(json_c_visit_userfunc)(json_object *jso, int flags, json_object *parent_jso, + const char *jso_key, size_t *jso_index, void *userarg); + +/** + * Visit each object in the JSON hierarchy starting at jso. + * For each object, userfunc is called, passing the object and userarg. + * If the object has a parent (i.e. anything other than jso itself) + * its parent will be passed as parent_jso, and either jso_key or jso_index + * will be set, depending on whether the parent is an object or an array. + * + * Nodes will be visited depth first, but containers (arrays and objects) + * will be visited twice, the second time with JSON_C_VISIT_SECOND set in + * flags. + * + * userfunc must return one of the defined return values, to indicate + * whether and how to continue visiting nodes, or one of various ways to stop. + * + * Returns 0 if nodes were visited successfully, even if some were + * intentionally skipped due to what userfunc returned. + * Returns <0 if an error occurred during iteration, including if + * userfunc returned JSON_C_VISIT_RETURN_ERROR. + */ +JSON_EXPORT int json_c_visit(json_object *jso, int future_flags, json_c_visit_userfunc *userfunc, + void *userarg); + +/** + * Passed to json_c_visit_userfunc as one of the flags values to indicate + * that this is the second time a container (array or object) is being + * called, after all of it's members have been iterated over. + */ +#define JSON_C_VISIT_SECOND 0x02 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should proceed normally. + */ +#define JSON_C_VISIT_RETURN_CONTINUE 0 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * over the members of the current object should be skipped. + * If the current object isn't a container (array or object), this + * is no different than JSON_C_VISIT_RETURN_CONTINUE. + */ +#define JSON_C_VISIT_RETURN_SKIP 7547 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * of the fields/elements of the containing object should stop + * and continue "popped up" a level of the object hierarchy. + * For example, returning this when handling arg will result in + * arg3 and any other fields being skipped. The next call to userfunc + * will be the JSON_C_VISIT_SECOND call on "foo", followed by a userfunc + * call on "bar". + *
+ * {
+ *   "foo": {
+ *     "arg1": 1,
+ *     "arg2": 2,
+ *     "arg3": 3,
+ *     ...
+ *   },
+ *   "bar": {
+ *     ...
+ *   }
+ * }
+ * 
+ */ +#define JSON_C_VISIT_RETURN_POP 767 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should stop immediately, and cause json_c_visit to return success. + */ +#define JSON_C_VISIT_RETURN_STOP 7867 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should stop immediately, and cause json_c_visit to return an error. + */ +#define JSON_C_VISIT_RETURN_ERROR -1 + +#ifdef __cplusplus +} +#endif + +#endif /* _json_c_json_visit_h_ */ diff --git a/comm/third_party/json-c/libjson.c b/comm/third_party/json-c/libjson.c new file mode 100644 index 0000000000..83d0a87fda --- /dev/null +++ b/comm/third_party/json-c/libjson.c @@ -0,0 +1,26 @@ + +/* dummy source file for compatibility purposes */ + +#if defined(HAVE_CDEFS_H) +#include +#endif + +#ifndef __warn_references + +#if defined(__GNUC__) && defined(HAS_GNU_WARNING_LONG) + +#define __warn_references(sym, msg) \ + __asm__(".section .gnu" #sym ",\n\t.ascii \"" msg "\"\n\t.text"); + +#else +#define __warn_references(sym, msg) /* nothing */ +#endif + +#endif + +#include "json_object.h" + +__warn_references(json_object_get, "Warning: please link against libjson-c instead of libjson"); + +/* __asm__(".section .gnu.warning." __STRING(sym) \ + " ; .ascii \"" msg "\" ; .text") */ diff --git a/comm/third_party/json-c/linkhash.c b/comm/third_party/json-c/linkhash.c new file mode 100644 index 0000000000..5e12c51e7c --- /dev/null +++ b/comm/third_party/json-c/linkhash.c @@ -0,0 +1,716 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ENDIAN_H +#include /* attempt to define endianness */ +#endif + +#if defined(_MSC_VER) || defined(__MINGW32__) +#define WIN32_LEAN_AND_MEAN +#include /* Get InterlockedCompareExchange */ +#endif + +#include "linkhash.h" +#include "random_seed.h" + +/* hash functions */ +static unsigned long lh_char_hash(const void *k); +static unsigned long lh_perllike_str_hash(const void *k); +static lh_hash_fn *char_hash_fn = lh_char_hash; + +/* comparison functions */ +int lh_char_equal(const void *k1, const void *k2); +int lh_ptr_equal(const void *k1, const void *k2); + +int json_global_set_string_hash(const int h) +{ + switch (h) + { + case JSON_C_STR_HASH_DFLT: char_hash_fn = lh_char_hash; break; + case JSON_C_STR_HASH_PERLLIKE: char_hash_fn = lh_perllike_str_hash; break; + default: return -1; + } + return 0; +} + +static unsigned long lh_ptr_hash(const void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(const void *k1, const void *k2) +{ + return (k1 == k2); +} + +/* + * hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * https://burtleburtle.net/bob/c/lookup3.c + * minor modifications to make functions static so no symbols are exported + * minor modifications to compile with -Werror + */ + +/* +------------------------------------------------------------------------------- +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 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. +------------------------------------------------------------------------------- +*/ + +/* + * 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 https://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. +------------------------------------------------------------------------------- +*/ +/* clang-format off */ +#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; \ +} +/* clang-format on */ + +/* +------------------------------------------------------------------------------- +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 +------------------------------------------------------------------------------- +*/ +/* clang-format off */ +#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); \ +} +/* clang-format on */ + +/* +------------------------------------------------------------------------------- +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 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 + * noticeably faster for short strings (like English words). + * AddressSanitizer is similarly picky about overrunning + * the buffer. (https://clang.llvm.org/docs/AddressSanitizer.html) + */ +#ifdef VALGRIND +#define PRECISE_MEMORY_ACCESS 1 +#elif defined(__SANITIZE_ADDRESS__) /* GCC's ASAN */ +#define PRECISE_MEMORY_ACCESS 1 +#elif defined(__has_feature) +#if __has_feature(address_sanitizer) /* Clang's ASAN */ +#define PRECISE_MEMORY_ACCESS 1 +#endif +#endif +#ifndef PRECISE_MEMORY_ACCESS + + 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 */ + + const uint8_t *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; /* FALLTHRU */ + case 11: c+=((uint32_t)k[10])<<16; /* FALLTHRU */ + case 10: c+=((uint32_t)k[9])<<8; /* FALLTHRU */ + case 9 : c+=k[8]; /* FALLTHRU */ + case 8 : b+=((uint32_t)k[7])<<24; /* FALLTHRU */ + case 7 : b+=((uint32_t)k[6])<<16; /* FALLTHRU */ + case 6 : b+=((uint32_t)k[5])<<8; /* FALLTHRU */ + case 5 : b+=k[4]; /* FALLTHRU */ + case 4 : a+=((uint32_t)k[3])<<24; /* FALLTHRU */ + case 3 : a+=((uint32_t)k[2])<<16; /* FALLTHRU */ + case 2 : a+=((uint32_t)k[1])<<8; /* FALLTHRU */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} +/* clang-format on */ + +/* a simple hash function similar to what perl does for strings. + * for good results, the string should not be excessively large. + */ +static unsigned long lh_perllike_str_hash(const void *k) +{ + const char *rkey = (const char *)k; + unsigned hashval = 1; + + while (*rkey) + hashval = hashval * 33 + *rkey++; + + return hashval; +} + +static unsigned long lh_char_hash(const void *k) +{ +#if defined _MSC_VER || defined __MINGW32__ +#define RANDOM_SEED_TYPE LONG +#else +#define RANDOM_SEED_TYPE int +#endif + static volatile RANDOM_SEED_TYPE random_seed = -1; + + if (random_seed == -1) + { + RANDOM_SEED_TYPE seed; + /* we can't use -1 as it is the uninitialized sentinel */ + while ((seed = json_c_get_random_seed()) == -1) {} +#if SIZEOF_INT == 8 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if SIZEOF_INT == 4 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if SIZEOF_INT == 2 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if defined USE_SYNC_COMPARE_AND_SWAP + (void)__sync_val_compare_and_swap(&random_seed, -1, seed); +#elif defined _MSC_VER || defined __MINGW32__ + InterlockedCompareExchange(&random_seed, seed, -1); +#else + //#warning "racy random seed initialization if used by multiple threads" + random_seed = seed; /* potentially racy */ +#endif + } + + return hashlittle((const char *)k, strlen((const char *)k), (uint32_t)random_seed); +} + +int lh_char_equal(const void *k1, const void *k2) +{ + return (strcmp((const char *)k1, (const char *)k2) == 0); +} + +struct lh_table *lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + /* Allocate space for elements to avoid divisions by zero. */ + assert(size > 0); + t = (struct lh_table *)calloc(1, sizeof(struct lh_table)); + if (!t) + return NULL; + + t->count = 0; + t->size = size; + t->table = (struct lh_entry *)calloc(size, sizeof(struct lh_entry)); + if (!t->table) + { + free(t); + return NULL; + } + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for (i = 0; i < size; i++) + t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table *lh_kchar_table_new(int size, lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, free_fn, char_hash_fn, lh_char_equal); +} + +struct lh_table *lh_kptr_table_new(int size, lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +int lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, NULL, t->hash_fn, t->equal_fn); + if (new_t == NULL) + return -1; + + for (ent = t->head; ent != NULL; ent = ent->next) + { + unsigned long h = lh_get_hash(new_t, ent->k); + unsigned int opts = 0; + if (ent->k_is_constant) + opts = JSON_C_OBJECT_ADD_CONSTANT_KEY; + if (lh_table_insert_w_hash(new_t, ent->k, ent->v, h, opts) != 0) + { + lh_table_free(new_t); + return -1; + } + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + free(new_t); + + return 0; +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + if (t->free_fn) + { + for (c = t->head; c != NULL; c = c->next) + t->free_fn(c); + } + free(t->table); + free(t); +} + +int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, const unsigned long h, + const unsigned opts) +{ + unsigned long n; + + if (t->count >= t->size * LH_LOAD_FACTOR) + { + /* Avoid signed integer overflow with large tables. */ + int new_size = (t->size > INT_MAX / 2) ? INT_MAX : (t->size * 2); + if (t->size == INT_MAX || lh_table_resize(t, new_size) != 0) + return -1; + } + + n = h % t->size; + + while (1) + { + if (t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) + break; + if ((int)++n == t->size) + n = 0; + } + + t->table[n].k = k; + t->table[n].k_is_constant = (opts & JSON_C_OBJECT_ADD_CONSTANT_KEY); + t->table[n].v = v; + t->count++; + + if (t->head == NULL) + { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } + else + { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} +int lh_table_insert(struct lh_table *t, const void *k, const void *v) +{ + return lh_table_insert_w_hash(t, k, v, lh_get_hash(t, k), 0); +} + +struct lh_entry *lh_table_lookup_entry_w_hash(struct lh_table *t, const void *k, + const unsigned long h) +{ + unsigned long n = h % t->size; + int count = 0; + + while (count < t->size) + { + if (t->table[n].k == LH_EMPTY) + return NULL; + if (t->table[n].k != LH_FREED && t->equal_fn(t->table[n].k, k)) + return &t->table[n]; + if ((int)++n == t->size) + n = 0; + count++; + } + return NULL; +} + +struct lh_entry *lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + return lh_table_lookup_entry_w_hash(t, k, lh_get_hash(t, k)); +} + +json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) + { + if (v != NULL) + *v = lh_entry_v(e); + return 1; /* key found */ + } + if (v != NULL) + *v = NULL; + return 0; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + ptrdiff_t n = (ptrdiff_t)(e - t->table); + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if (n < 0) + { + return -2; + } + + if (t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) + return -1; + t->count--; + if (t->free_fn) + t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if (t->tail == &t->table[n] && t->head == &t->table[n]) + { + t->head = t->tail = NULL; + } + else if (t->head == &t->table[n]) + { + t->head->next->prev = NULL; + t->head = t->head->next; + } + else if (t->tail == &t->table[n]) + { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } + else + { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (!e) + return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} diff --git a/comm/third_party/json-c/linkhash.h b/comm/third_party/json-c/linkhash.h new file mode 100644 index 0000000000..5e5e240822 --- /dev/null +++ b/comm/third_party/json-c/linkhash.h @@ -0,0 +1,447 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Internal methods for working with json_type_object objects. Although + * this is exposed by the json_object_get_object() function and within the + * json_object_iter type, it is not recommended for direct use. + */ +#ifndef _json_c_linkhash_h_ +#define _json_c_linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void *)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void *)-2 + +/** + * default string hash function + */ +#define JSON_C_STR_HASH_DFLT 0 + +/** + * perl-like string hash function + */ +#define JSON_C_STR_HASH_PERLLIKE 1 + +/** + * This function sets the hash function to be used for strings. + * Must be one of the JSON_C_STR_HASH_* values. + * @returns 0 - ok, -1 if parameter was invalid + */ +int json_global_set_string_hash(const int h); + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void(lh_entry_free_fn)(struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long(lh_hash_fn)(const void *k); +/** + * callback function prototypes + */ +typedef int(lh_equal_fn)(const void *k1, const void *k2); + +/** + * An entry in the hash table. Outside of linkhash.c, treat this as opaque. + */ +struct lh_entry +{ + /** + * The key. + * @deprecated Use lh_entry_k() instead of accessing this directly. + */ + const void *k; + /** + * A flag for users of linkhash to know whether or not they + * need to free k. + * @deprecated use lh_entry_k_is_constant() instead. + */ + int k_is_constant; + /** + * The value. + * @deprecated Use lh_entry_v() instead of accessing this directly. + */ + const void *v; + /** + * The next entry. + * @deprecated Use lh_entry_next() instead of accessing this directly. + */ + struct lh_entry *next; + /** + * The previous entry. + * @deprecated Use lh_entry_prev() instead of accessing this directly. + */ + struct lh_entry *prev; +}; + +/** + * The hash table structure. Outside of linkhash.c, treat this as opaque. + */ +struct lh_table +{ + /** + * Size of our hash. + * @deprecated do not use outside of linkhash.c + */ + int size; + /** + * Numbers of entries. + * @deprecated Use lh_table_length() instead. + */ + int count; + + /** + * The first entry. + * @deprecated Use lh_table_head() instead. + */ + struct lh_entry *head; + + /** + * The last entry. + * @deprecated Do not use, may be removed in a future release. + */ + struct lh_entry *tail; + + /** + * Internal storage of the actual table of entries. + * @deprecated do not use outside of linkhash.c + */ + struct lh_entry *table; + + /** + * A pointer to the function responsible for freeing an entry. + * @deprecated do not use outside of linkhash.c + */ + lh_entry_free_fn *free_fn; + /** + * @deprecated do not use outside of linkhash.c + */ + lh_hash_fn *hash_fn; + /** + * @deprecated do not use outside of linkhash.c + */ + lh_equal_fn *equal_fn; +}; +typedef struct lh_table lh_table; + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) for (entry = lh_table_head(table); entry; entry = lh_entry_next(entry)) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + * + * @param table a struct lh_table * to iterate over + * @param entry a struct lh_entry * variable to hold each element + * @param tmp a struct lh_entry * variable to hold a temporary pointer to the next element + */ +#define lh_foreach_safe(table, entry, tmp) \ + for (entry = lh_table_head(table); entry && ((tmp = lh_entry_next(entry)) || 1); entry = tmp) + +/** + * Create a new linkhash table. + * + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table *lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash table with char keys. + * + * @param size initial table size. + * @param free_fn callback function used to free memory for entries. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table *lh_kchar_table_new(int size, lh_entry_free_fn *free_fn); + +/** + * Convenience function to create a new linkhash table with ptr keys. + * + * @param size initial table size. + * @param free_fn callback function used to free memory for entries. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table *lh_kptr_table_new(int size, lh_entry_free_fn *free_fn); + +/** + * Free a linkhash table. + * + * If a lh_entry_free_fn callback free function was provided then it is + * called for all entries in the table. + * + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + +/** + * Insert a record into the table. + * + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +extern int lh_table_insert(struct lh_table *t, const void *k, const void *v); + +/** + * Insert a record into the table using a precalculated key hash. + * + * The hash h, which should be calculated with lh_get_hash() on k, is provided by + * the caller, to allow for optimization when multiple operations with the same + * key are known to be needed. + * + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + * @param h hash value of the key to insert + * @param opts if set to JSON_C_OBJECT_ADD_CONSTANT_KEY, sets lh_entry.k_is_constant + * so t's free function knows to avoid freeing the key. + */ +extern int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, + const unsigned long h, const unsigned opts); + +/** + * Lookup a record in the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry *lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record in the table using a precalculated key hash. + * + * The hash h, which should be calculated with lh_get_hash() on k, is provided by + * the caller, to allow for optimization when multiple operations with the same + * key are known to be needed. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param h hash value of the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry *lh_table_lookup_entry_w_hash(struct lh_table *t, const void *k, + const unsigned long h); + +/** + * Lookup a record in the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + +/** + * Delete a record from the table. + * + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +/** + * Return the number of entries in the table. + */ +extern int lh_table_length(struct lh_table *t); + +/** + * Resizes the specified table. + * + * @param t Pointer to table to resize. + * @param new_size New table size. Must be positive. + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +int lh_table_resize(struct lh_table *t, int new_size); + +/** + * @deprecated Don't use this outside of linkhash.h: + */ +#if (defined(AIX_CC) || (defined(_MSC_VER) && (_MSC_VER <= 1800)) ) +/* VS2010 can't handle inline funcs, so skip it there */ +#define _LH_INLINE +#else +#define _LH_INLINE inline +#endif + +/** + * Return the first entry in the lh_table. + * @see lh_entry_next() + */ +static _LH_INLINE struct lh_entry *lh_table_head(const lh_table *t) +{ + return t->head; +} + +/** + * Calculate the hash of a key for a given table. + * + * This is an extension to support functions that need to calculate + * the hash several times and allows them to do it just once and then pass + * in the hash to all utility functions. Depending on use case, this can be a + * considerable performance improvement. + * @param t the table (used to obtain hash function) + * @param k a pointer to the key to lookup + * @return the key's hash + */ +static _LH_INLINE unsigned long lh_get_hash(const struct lh_table *t, const void *k) +{ + return t->hash_fn(k); +} + + +/** + * @deprecated Don't use this outside of linkhash.h: + */ +#ifdef __UNCONST +#define _LH_UNCONST(a) __UNCONST(a) +#else +#define _LH_UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) +#endif + +/** + * Return a non-const version of lh_entry.k. + * + * lh_entry.k is const to indicate and help ensure that linkhash itself doesn't modify + * it, but callers are allowed to do what they want with it. + * @see lh_entry_k_is_constant() + */ +static _LH_INLINE void *lh_entry_k(const struct lh_entry *e) +{ + return _LH_UNCONST(e->k); +} + +/** + * Returns 1 if the key for the given entry is constant, and thus + * does not need to be freed when the lh_entry is freed. + * @see lh_table_insert_w_hash() + */ +static _LH_INLINE int lh_entry_k_is_constant(const struct lh_entry *e) +{ + return e->k_is_constant; +} + +/** + * Return a non-const version of lh_entry.v. + * + * v is const to indicate and help ensure that linkhash itself doesn't modify + * it, but callers are allowed to do what they want with it. + */ +static _LH_INLINE void *lh_entry_v(const struct lh_entry *e) +{ + return _LH_UNCONST(e->v); +} + +/** + * Change the value for an entry. The caller is responsible for freeing + * the previous value. + */ +static _LH_INLINE void lh_entry_set_val(struct lh_entry *e, void *newval) +{ + e->v = newval; +} + +/** + * Return the next element, or NULL if there is no next element. + * @see lh_table_head() + * @see lh_entry_prev() + */ +static _LH_INLINE struct lh_entry *lh_entry_next(const struct lh_entry *e) +{ + return e->next; +} + +/** + * Return the previous element, or NULL if there is no previous element. + * @see lh_table_head() + * @see lh_entry_next() + */ +static _LH_INLINE struct lh_entry *lh_entry_prev(const struct lh_entry *e) +{ + return e->prev; +} + +#undef _LH_INLINE + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/math_compat.h b/comm/third_party/json-c/math_compat.h new file mode 100644 index 0000000000..2382fe15b3 --- /dev/null +++ b/comm/third_party/json-c/math_compat.h @@ -0,0 +1,43 @@ +#ifndef __math_compat_h +#define __math_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* Define isnan, isinf, infinity and nan on Windows/MSVC */ + +#ifndef HAVE_DECL_ISNAN +#ifdef HAVE_DECL__ISNAN +#include +#define isnan(x) _isnan(x) +#else +/* On platforms like AIX and "IBM i" we need to provide our own isnan */ +#define isnan(x) ((x) != (x)) +#endif +#endif + +#ifndef HAVE_DECL_ISINF +#ifdef HAVE_DECL__FINITE +#include +#define isinf(x) (!_finite(x)) +#else +#include +/* On platforms like AIX and "IBM i" we need to provide our own isinf */ +#define isinf(x) ((x) < -DBL_MAX || (x) > DBL_MAX) +#endif +#endif + +#ifndef HAVE_DECL_INFINITY +#include +#define INFINITY (DBL_MAX + DBL_MAX) +#define HAVE_DECL_INFINITY +#endif + +#ifndef HAVE_DECL_NAN +#define NAN (INFINITY - INFINITY) +#define HAVE_DECL_NAN +#endif + +#endif diff --git a/comm/third_party/json-c/moz.build b/comm/third_party/json-c/moz.build new file mode 100644 index 0000000000..788f9856db --- /dev/null +++ b/comm/third_party/json-c/moz.build @@ -0,0 +1,52 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Library("json-c") +FINAL_LIBRARY = "rnp" + +# Honor --with-system-jsonc +if CONFIG["MZLA_SYSTEM_JSONC"]: + OS_LIBS += CONFIG["MZLA_JSONC_LIBS"] +else: + include("../rnpdefs.mozbuild") + + if CONFIG["CC_TYPE"] == "clang-cl": + COMPILE_FLAGS["WARNINGS_CFLAGS"] += [ + "-Wno-macro-redefined", + ] + + DEFINES["_GNU_SOURCE"] = True + DEFINES["CC"] = CONFIG["_CC"] + + CONFIGURE_DEFINE_FILES += ["config.h", "json_config.h"] + + GeneratedFile( + "json.h", + script="/python/mozbuild/mozbuild/action/preprocessor.py", + entry_point="generate", + inputs=["json.h.cmakein"], + flags=[ + "--marker", "%", + "-F", "substitution", + "-DJSON_H_JSON_POINTER=#include \"json_pointer.h\"" + ] + ) + + SOURCES += [ + "arraylist.c", + "debug.c", + "json_c_version.c", + "json_object.c", + "json_object_iterator.c", + "json_pointer.c", + "json_tokener.c", + "json_util.c", + "json_visit.c", + "linkhash.c", + "printbuf.c", + "random_seed.c", + "strerror_override.c", + ] diff --git a/comm/third_party/json-c/printbuf.c b/comm/third_party/json-c/printbuf.c new file mode 100644 index 0000000000..a08f7b1582 --- /dev/null +++ b/comm/third_party/json-c/printbuf.c @@ -0,0 +1,180 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (https://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_STDARG_H +#include +#else /* !HAVE_STDARG_H */ +#error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "debug.h" +#include "printbuf.h" +#include "snprintf_compat.h" +#include "vasprintf_compat.h" + +static int printbuf_extend(struct printbuf *p, int min_size); + +struct printbuf *printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf *)calloc(1, sizeof(struct printbuf)); + if (!p) + return NULL; + p->size = 32; + p->bpos = 0; + if (!(p->buf = (char *)malloc(p->size))) + { + free(p); + return NULL; + } + p->buf[0] = '\0'; + return p; +} + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, int min_size) +{ + char *t; + int new_size; + + if (p->size >= min_size) + return 0; + /* Prevent signed integer overflows with large buffers. */ + if (min_size > INT_MAX - 8) + return -1; + if (p->size > INT_MAX / 2) + new_size = min_size + 8; + else { + new_size = p->size * 2; + if (new_size < min_size + 8) + new_size = min_size + 8; + } +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if (!(t = (char *)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +int printbuf_memappend(struct printbuf *p, const char *buf, int size) +{ + /* Prevent signed integer overflows with large buffers. */ + if (size < 0 || size > INT_MAX - p->bpos - 1) + return -1; + if (p->size <= p->bpos + size + 1) + { + if (printbuf_extend(p, p->bpos + size + 1) < 0) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos] = '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) +{ + int size_needed; + + if (offset == -1) + offset = pb->bpos; + /* Prevent signed integer overflows with large buffers. */ + if (len < 0 || offset < -1 || len > INT_MAX - offset) + return -1; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + if (pb->bpos < offset) + memset(pb->buf + pb->bpos, '\0', offset - pb->bpos); + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + return 0; +} + +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size; + char buf[128]; + + /* use stack buffer first */ + va_start(ap, msg); + size = vsnprintf(buf, 128, msg, ap); + va_end(ap); + /* if string is greater than stack buffer, then use dynamic string + * with vasprintf. Note: some implementations of vsnprintf return -1 + * if output is truncated whereas some return the number of bytes that + * would have been written - this code handles both cases. + */ + if (size < 0 || size > 127) + { + va_start(ap, msg); + if ((size = vasprintf(&t, msg, ap)) < 0) + { + va_end(ap); + return -1; + } + va_end(ap); + size = printbuf_memappend(p, t, size); + free(t); + } + else + { + size = printbuf_memappend(p, buf, size); + } + return size; +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if (p) + { + free(p->buf); + free(p); + } +} diff --git a/comm/third_party/json-c/printbuf.h b/comm/third_party/json-c/printbuf.h new file mode 100644 index 0000000000..8dbf2c6ae1 --- /dev/null +++ b/comm/third_party/json-c/printbuf.h @@ -0,0 +1,131 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (https://www.opensource.org/licenses/mit-license.php) + */ + +/** + * @file + * @brief Internal string buffer handling. Unless you're writing a + * json_object_to_json_string_fn implementation for use with + * json_object_set_serializer() direct use of this is not + * recommended. + */ +#ifndef _json_c_printbuf_h_ +#define _json_c_printbuf_h_ + +#ifndef JSON_EXPORT +#if defined(_MSC_VER) && defined(JSON_C_DLL) +#define JSON_EXPORT __declspec(dllexport) +#else +#define JSON_EXPORT extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf +{ + char *buf; + int bpos; + int size; +}; +typedef struct printbuf printbuf; + +JSON_EXPORT struct printbuf *printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast() is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * + * Your code should not use printbuf_memappend() directly unless it + * checks the return code. Use printbuf_memappend_fast() instead. + */ +JSON_EXPORT int printbuf_memappend(struct printbuf *p, const char *buf, int size); + +#define printbuf_memappend_fast(p, bufptr, bufsize) \ + do \ + { \ + if ((p->size - p->bpos) > bufsize) \ + { \ + memcpy(p->buf + p->bpos, (bufptr), bufsize); \ + p->bpos += bufsize; \ + p->buf[p->bpos] = '\0'; \ + } \ + else \ + { \ + printbuf_memappend(p, (bufptr), bufsize); \ + } \ + } while (0) + +#define printbuf_length(p) ((p)->bpos) + +/** + * Results in a compile error if the argument is not a string literal. + */ +#define _printbuf_check_literal(mystr) ("" mystr) + +/** + * This is an optimization wrapper around printbuf_memappend() that is useful + * for appending string literals. Since the size of string constants is known + * at compile time, using this macro can avoid a costly strlen() call. This is + * especially helpful when a constant string must be appended many times. If + * you got here because of a compilation error caused by passing something + * other than a string literal, use printbuf_memappend_fast() in conjunction + * with strlen(). + * + * See also: + * printbuf_memappend_fast() + * printbuf_memappend() + * sprintbuf() + */ +#define printbuf_strappend(pb, str) \ + printbuf_memappend((pb), _printbuf_check_literal(str), sizeof(str) - 1) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +JSON_EXPORT int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); + +/** + * Formatted print to printbuf. + * + * This function is the most expensive of the available functions for appending + * string data to a printbuf and should be used only where convenience is more + * important than speed. Avoid using this function in high performance code or + * tight loops; in these scenarios, consider using snprintf() with a static + * buffer in conjunction with one of the printbuf_*append() functions. + * + * See also: + * printbuf_memappend_fast() + * printbuf_memappend() + * printbuf_strappend() + */ +JSON_EXPORT int sprintbuf(struct printbuf *p, const char *msg, ...); + +JSON_EXPORT void printbuf_reset(struct printbuf *p); + +JSON_EXPORT void printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/random_seed.c b/comm/third_party/json-c/random_seed.c new file mode 100644 index 0000000000..7945824c7a --- /dev/null +++ b/comm/third_party/json-c/random_seed.c @@ -0,0 +1,355 @@ +/* + * random_seed.c + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "random_seed.h" +#include "config.h" +#include "strerror_override.h" +#include +#include +#ifdef HAVE_BSD_STDLIB_H +#include +#endif + +#define DEBUG_SEED(s) + +#if defined(__APPLE__) || defined(__unix__) || defined(__linux__) +#define HAVE_DEV_RANDOM 1 +#endif + +#ifdef HAVE_ARC4RANDOM +#undef HAVE_GETRANDOM +#undef HAVE_DEV_RANDOM +#undef HAVE_CRYPTGENRANDOM +#endif + +#if defined ENABLE_RDRAND + +/* cpuid */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) +#define HAS_X86_CPUID 1 + +static void do_cpuid(int regs[], int h) +{ + /* clang-format off */ + __asm__ __volatile__("cpuid" + : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) + : "a"(h)); + /* clang-format on */ +} + +#elif defined _MSC_VER + +#define HAS_X86_CPUID 1 +#define do_cpuid __cpuid + +#endif + +/* has_rdrand */ + +#if HAS_X86_CPUID + +static int get_rdrand_seed(void); + +/* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */ +static int _has_rdrand = -1; + +static int has_rdrand(void) +{ + if (_has_rdrand != -1) + { + return _has_rdrand; + } + + /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */ + int regs[4]; + do_cpuid(regs, 1); + if (!(regs[2] & (1 << 30))) + { + _has_rdrand = 0; + return 0; + } + + /* + * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF + * unconditionally. To avoid locking up later, test RDRAND here. If over + * 3 trials RDRAND has returned the same value, declare it broken. + * Example CPUs are AMD Ryzen 3000 series + * and much older AMD APUs, such as the E1-1500 + * https://github.com/systemd/systemd/issues/11810 + * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend + */ + _has_rdrand = 0; + int prev = get_rdrand_seed(); + for (int i = 0; i < 3; i++) + { + int temp = get_rdrand_seed(); + if (temp != prev) + { + _has_rdrand = 1; + break; + } + + prev = temp; + } + + return _has_rdrand; +} + +#endif + +/* get_rdrand_seed - GCC x86 and X64 */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) + +#define HAVE_RDRAND 1 + +static int get_rdrand_seed(void) +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; + /* rdrand eax */ + /* clang-format off */ + __asm__ __volatile__("1: .byte 0x0F\n" + " .byte 0xC7\n" + " .byte 0xF0\n" + " jnc 1b;\n" + : "=a" (_eax)); + /* clang-format on */ + return _eax; +} + +#endif + +#if defined _MSC_VER + +#if _MSC_VER >= 1700 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2012 and above */ + +static int get_rdrand_seed(void) +{ + DEBUG_SEED("get_rdrand_seed"); + int r; + while (_rdrand32_step(&r) == 0) + ; + return r; +} + +#elif defined _M_IX86 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ + +/* clang-format off */ +static int get_rdrand_seed(void) +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; +retry: + /* rdrand eax */ + __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 + __asm jnc retry + __asm mov _eax, eax + return _eax; +} +/* clang-format on */ + +#endif +#endif + +#endif /* defined ENABLE_RDRAND */ + +#ifdef HAVE_GETRANDOM + +#include +#ifdef HAVE_SYS_RANDOM_H +#include +#endif + +static int get_getrandom_seed(int *seed) +{ + DEBUG_SEED("get_getrandom_seed"); + + ssize_t ret; + + do + { + ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK); + } while ((ret == -1) && (errno == EINTR)); + + if (ret == -1) + { + if (errno == ENOSYS) /* syscall not available in kernel */ + return -1; + if (errno == EAGAIN) /* entropy not yet initialized */ + return -1; + + fprintf(stderr, "error from getrandom(): %s", strerror(errno)); + return -1; + } + + if (ret != sizeof(*seed)) + return -1; + + return 0; +} +#endif /* defined HAVE_GETRANDOM */ + +/* get_dev_random_seed */ + +#ifdef HAVE_DEV_RANDOM + +#include +#include +#if HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#include + +static const char *dev_random_file = "/dev/urandom"; + +static int get_dev_random_seed(int *seed) +{ + DEBUG_SEED("get_dev_random_seed"); + + struct stat buf; + if (stat(dev_random_file, &buf)) + return -1; + if ((buf.st_mode & S_IFCHR) == 0) + return -1; + + int fd = open(dev_random_file, O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); + return -1; + } + + ssize_t nread = read(fd, seed, sizeof(*seed)); + + close(fd); + + if (nread != sizeof(*seed)) + { + fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno)); + return -1; + } + + return 0; +} + +#endif + +/* get_cryptgenrandom_seed */ + +#ifdef WIN32 + +#define HAVE_CRYPTGENRANDOM 1 + +/* clang-format off */ +#include + +/* Caution: these blank lines must remain so clang-format doesn't reorder + includes to put windows.h after wincrypt.h */ + +#include +/* clang-format on */ +#ifndef __GNUC__ +#pragma comment(lib, "advapi32.lib") +#endif + +static int get_cryptgenrandom_seed(int *seed) +{ + HCRYPTPROV hProvider = 0; + DWORD dwFlags = CRYPT_VERIFYCONTEXT; + + DEBUG_SEED("get_cryptgenrandom_seed"); + + /* WinNT 4 and Win98 do no support CRYPT_SILENT */ + if (LOBYTE(LOWORD(GetVersion())) > 4) + dwFlags |= CRYPT_SILENT; + + if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags)) + { + fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError()); + return -1; + } + else + { + BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed); + CryptReleaseContext(hProvider, 0); + if (!ret) + { + fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError()); + return -1; + } + } + + return 0; +} + +#endif + +/* get_time_seed */ + +#ifndef HAVE_ARC4RANDOM +#include + +static int get_time_seed(void) +{ + DEBUG_SEED("get_time_seed"); + + return (unsigned)time(NULL) * 433494437; +} +#endif + +/* json_c_get_random_seed */ + +int json_c_get_random_seed(void) +{ +#ifdef OVERRIDE_GET_RANDOM_SEED + OVERRIDE_GET_RANDOM_SEED; +#endif +#if defined HAVE_RDRAND && HAVE_RDRAND + if (has_rdrand()) + return get_rdrand_seed(); +#endif +#ifdef HAVE_ARC4RANDOM + /* arc4random never fails, so use it if it's available */ + return arc4random(); +#else +#ifdef HAVE_GETRANDOM + { + int seed = 0; + if (get_getrandom_seed(&seed) == 0) + return seed; + } +#endif +#if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM + { + int seed = 0; + if (get_dev_random_seed(&seed) == 0) + return seed; + } +#endif +#if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM + { + int seed = 0; + if (get_cryptgenrandom_seed(&seed) == 0) + return seed; + } +#endif + return get_time_seed(); +#endif /* !HAVE_ARC4RANDOM */ +} diff --git a/comm/third_party/json-c/random_seed.h b/comm/third_party/json-c/random_seed.h new file mode 100644 index 0000000000..72ee5f6e85 --- /dev/null +++ b/comm/third_party/json-c/random_seed.h @@ -0,0 +1,29 @@ +/* + * random_seed.h + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef seed_h +#define seed_h + +#ifdef __cplusplus +extern "C" { +#endif + +extern int json_c_get_random_seed(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/comm/third_party/json-c/snprintf_compat.h b/comm/third_party/json-c/snprintf_compat.h new file mode 100644 index 0000000000..76f7a6ce22 --- /dev/null +++ b/comm/third_party/json-c/snprintf_compat.h @@ -0,0 +1,41 @@ +#ifndef __snprintf_compat_h +#define __snprintf_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* + * Microsoft's _vsnprintf and _snprint don't always terminate + * the string, so use wrappers that ensure that. + */ + +#include + +#if !defined(HAVE_SNPRINTF) && (defined(_MSC_VER) || defined(__MINGW32__)) +static int json_c_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + int ret; + ret = _vsnprintf(str, size, format, ap); + str[size - 1] = '\0'; + return ret; +} +#define vsnprintf json_c_vsnprintf + +static int json_c_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + ret = json_c_vsnprintf(str, size, format, ap); + va_end(ap); + return ret; +} +#define snprintf json_c_snprintf + +#elif !defined(HAVE_SNPRINTF) /* !HAVE_SNPRINTF */ +#error Need vsnprintf! +#endif /* !HAVE_SNPRINTF && defined(WIN32) */ + +#endif /* __snprintf_compat_h */ diff --git a/comm/third_party/json-c/strdup_compat.h b/comm/third_party/json-c/strdup_compat.h new file mode 100644 index 0000000000..2f2df65a0d --- /dev/null +++ b/comm/third_party/json-c/strdup_compat.h @@ -0,0 +1,16 @@ +#ifndef __strdup_compat_h +#define __strdup_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) +/* MSC has the version as _strdup */ +#define strdup _strdup +#elif !defined(HAVE_STRDUP) +#error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#endif diff --git a/comm/third_party/json-c/strerror_override.c b/comm/third_party/json-c/strerror_override.c new file mode 100644 index 0000000000..a3dd377a3d --- /dev/null +++ b/comm/third_party/json-c/strerror_override.c @@ -0,0 +1,110 @@ +#define STRERROR_OVERRIDE_IMPL 1 +#include "strerror_override.h" + +/* + * Override strerror() to get consistent output across platforms. + */ + +static struct +{ + int errno_value; + const char *errno_str; +} errno_list[] = { +/* clang-format off */ +#define STRINGIFY(x) #x +#define ENTRY(x) {x, &STRINGIFY(undef_ ## x)[6]} + ENTRY(EPERM), + ENTRY(ENOENT), + ENTRY(ESRCH), + ENTRY(EINTR), + ENTRY(EIO), + ENTRY(ENXIO), + ENTRY(E2BIG), +#ifdef ENOEXEC + ENTRY(ENOEXEC), +#endif + ENTRY(EBADF), + ENTRY(ECHILD), + ENTRY(EDEADLK), + ENTRY(ENOMEM), + ENTRY(EACCES), + ENTRY(EFAULT), +#ifdef ENOTBLK + ENTRY(ENOTBLK), +#endif + ENTRY(EBUSY), + ENTRY(EEXIST), + ENTRY(EXDEV), + ENTRY(ENODEV), + ENTRY(ENOTDIR), + ENTRY(EISDIR), + ENTRY(EINVAL), + ENTRY(ENFILE), + ENTRY(EMFILE), + ENTRY(ENOTTY), +#ifdef ETXTBSY + ENTRY(ETXTBSY), +#endif + ENTRY(EFBIG), + ENTRY(ENOSPC), + ENTRY(ESPIPE), + ENTRY(EROFS), + ENTRY(EMLINK), + ENTRY(EPIPE), + ENTRY(EDOM), + ENTRY(ERANGE), + ENTRY(EAGAIN), + { 0, (char *)0 } +}; +/* clang-format on */ + +// Enabled during tests +static int _json_c_strerror_enable = 0; +extern char *getenv(const char *name); // Avoid including stdlib.h + +#define PREFIX "ERRNO=" +static char errno_buf[128] = PREFIX; +char *_json_c_strerror(int errno_in) +{ + int start_idx; + char digbuf[20]; + int ii, jj; + + if (!_json_c_strerror_enable) + _json_c_strerror_enable = (getenv("_JSON_C_STRERROR_ENABLE") == NULL) ? -1 : 1; + if (_json_c_strerror_enable == -1) + return strerror(errno_in); + + // Avoid standard functions, so we don't need to include any + // headers, or guess at signatures. + + for (ii = 0; errno_list[ii].errno_str != (char *)0; ii++) + { + const char *errno_str = errno_list[ii].errno_str; + if (errno_list[ii].errno_value != errno_in) + continue; + + for (start_idx = sizeof(PREFIX) - 1, jj = 0; errno_str[jj] != '\0'; + jj++, start_idx++) + { + errno_buf[start_idx] = errno_str[jj]; + } + errno_buf[start_idx] = '\0'; + return errno_buf; + } + + // It's not one of the known errno values, return the numeric value. + for (ii = 0; errno_in >= 10; errno_in /= 10, ii++) + { + digbuf[ii] = "0123456789"[(errno_in % 10)]; + } + digbuf[ii] = "0123456789"[(errno_in % 10)]; + + // Reverse the digits + for (start_idx = sizeof(PREFIX) - 1; ii >= 0; ii--, start_idx++) + { + errno_buf[start_idx] = digbuf[ii]; + } + errno_buf[start_idx] = '\0'; + return errno_buf; +} diff --git a/comm/third_party/json-c/strerror_override.h b/comm/third_party/json-c/strerror_override.h new file mode 100644 index 0000000000..0b04eb4cab --- /dev/null +++ b/comm/third_party/json-c/strerror_override.h @@ -0,0 +1,30 @@ +#ifndef _json_strerror_override_h_ +#define _json_strerror_override_h_ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "config.h" +#include + +#include "json_object.h" /* for JSON_EXPORT */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +JSON_EXPORT char *_json_c_strerror(int errno_in); + +#ifndef STRERROR_OVERRIDE_IMPL +#define strerror _json_c_strerror +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _json_strerror_override_h_ */ diff --git a/comm/third_party/json-c/strerror_override_private.h b/comm/third_party/json-c/strerror_override_private.h new file mode 100644 index 0000000000..8726e59fc3 --- /dev/null +++ b/comm/third_party/json-c/strerror_override_private.h @@ -0,0 +1,14 @@ +#ifndef __json_strerror_override_private_h__ +#define __json_strerror_override_private_h__ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "json_types.h" + +/* Used by tests to get consistent output */ +JSON_EXPORT int _json_c_strerror_enable; + +#endif diff --git a/comm/third_party/json-c/vasprintf_compat.h b/comm/third_party/json-c/vasprintf_compat.h new file mode 100644 index 0000000000..59b2e96074 --- /dev/null +++ b/comm/third_party/json-c/vasprintf_compat.h @@ -0,0 +1,67 @@ +#ifndef __vasprintf_compat_h +#define __vasprintf_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "snprintf_compat.h" + +#ifndef WIN32 +#include +#endif /* !defined(WIN32) */ +#include +#include + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; + va_list ap2; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if (!buf) + { + return -1; + } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap); +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + * our buffer like on some 64bit sun systems... but hey, it's time to move on + */ + va_copy(ap2, ap); + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap2); + va_end(ap2); +#endif /* defined(WIN32) */ + if (chars < 0 || (size_t)chars + 1 > SIZE_MAX / sizeof(char)) + { + return -1; + } + + b = (char *)malloc(sizeof(char) * ((size_t)chars + 1)); + if (!b) + { + return -1; + } + + if ((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } + else + { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +#endif /* __vasprintf_compat_h */ -- cgit v1.2.3