From e3c4c21ccec9fc8f03bcbdf914a6bb3037711d51 Mon Sep 17 00:00:00 2001 From: Netrunner Date: Fri, 10 May 2024 00:54:36 +0100 Subject: [PATCH] shogdb: replace jmespath json with jansson and reimplement JSONPath filter syntax --- lib/jansson/.gitignore | 37 + lib/jansson/Android.mk | 30 + lib/jansson/CHANGES | 1008 +++ lib/jansson/CMakeLists.txt | 663 ++ lib/jansson/CONTRIBUTING.md | 3 + lib/jansson/CleanSpec.mk | 49 + lib/jansson/LICENSE | 43 + lib/jansson/Makefile.am | 12 + lib/jansson/README.rst | 70 + lib/jansson/SECURITY.md | 9 + lib/jansson/android/jansson_config.h | 39 + lib/jansson/cmake/CheckFunctionKeywords.cmake | 15 + lib/jansson/cmake/CodeCoverage.cmake | 163 + lib/jansson/cmake/FindSphinx.cmake | 315 + lib/jansson/cmake/janssonConfig.cmake.in | 4 + lib/jansson/cmake/jansson_config.h.cmake | 72 + .../cmake/jansson_private_config.h.cmake | 62 + lib/jansson/configure.ac | 181 + lib/jansson/doc/.gitignore | 1 + lib/jansson/doc/Makefile.am | 20 + lib/jansson/doc/README | 5 + lib/jansson/doc/apiref.rst | 2064 ++++++ lib/jansson/doc/changes.rst | 5 + lib/jansson/doc/conf.py | 217 + lib/jansson/doc/conformance.rst | 119 + lib/jansson/doc/ext/refcounting.py | 69 + lib/jansson/doc/gettingstarted.rst | 264 + lib/jansson/doc/github_commits.c | 181 + lib/jansson/doc/index.rst | 53 + lib/jansson/doc/threadsafety.rst | 82 + lib/jansson/doc/tutorial.rst | 288 + lib/jansson/doc/upgrading.rst | 76 + lib/jansson/examples/README.rst | 4 + lib/jansson/examples/simple_parse.c | 200 + lib/jansson/jansson.pc.in | 10 + lib/jansson/release.sh | 70 + lib/jansson/scripts/clang-format | 3 + lib/jansson/scripts/clang-format-check | 30 + lib/jansson/src/Makefile.am | 35 + lib/jansson/src/dtoa.c | 6265 +++++++++++++++++ lib/jansson/src/dump.c | 490 ++ lib/jansson/src/error.c | 59 + lib/jansson/src/hashtable.c | 340 + lib/jansson/src/hashtable.h | 186 + lib/jansson/src/hashtable_seed.c | 277 + lib/jansson/src/jansson.def | 83 + lib/jansson/src/jansson.h | 419 ++ lib/jansson/src/jansson_config.h.in | 47 + lib/jansson/src/jansson_private.h | 114 + lib/jansson/src/load.c | 1106 +++ lib/jansson/src/lookup3.h | 382 + lib/jansson/src/memory.c | 61 + lib/jansson/src/pack_unpack.c | 936 +++ lib/jansson/src/strbuffer.c | 98 + lib/jansson/src/strbuffer.h | 35 + lib/jansson/src/strconv.c | 237 + lib/jansson/src/utf.c | 159 + lib/jansson/src/utf.h | 28 + lib/jansson/src/value.c | 1120 +++ lib/jansson/src/version.c | 28 + lib/jansson/test/.gitignore | 21 + lib/jansson/test/Makefile.am | 10 + lib/jansson/test/bin/Makefile.am | 5 + lib/jansson/test/bin/json_process.c | 261 + lib/jansson/test/ossfuzz/.gitignore | 1 + lib/jansson/test/ossfuzz/Makefile.am | 32 + .../test/ossfuzz/json_load_dump_fuzzer.cc | 132 + lib/jansson/test/ossfuzz/ossfuzz.sh | 30 + lib/jansson/test/ossfuzz/standaloneengine.cc | 74 + lib/jansson/test/ossfuzz/testinput.h | 3 + lib/jansson/test/run-suites | 50 + lib/jansson/test/scripts/run-tests.sh | 100 + lib/jansson/test/scripts/valgrind.sh | 35 + lib/jansson/test/suites/.gitattributes | 2 + lib/jansson/test/suites/Makefile.am | 2 + lib/jansson/test/suites/api/Makefile.am | 42 + lib/jansson/test/suites/api/check-exports | 23 + lib/jansson/test/suites/api/run | 36 + lib/jansson/test/suites/api/test_array.c | 484 ++ lib/jansson/test/suites/api/test_chaos.c | 168 + lib/jansson/test/suites/api/test_copy.c | 375 + lib/jansson/test/suites/api/test_dump.c | 311 + .../test/suites/api/test_dump_callback.c | 82 + lib/jansson/test/suites/api/test_equal.c | 202 + lib/jansson/test/suites/api/test_fixed_size.c | 228 + lib/jansson/test/suites/api/test_load.c | 238 + .../test/suites/api/test_load_callback.c | 77 + lib/jansson/test/suites/api/test_loadb.c | 37 + .../test/suites/api/test_memory_funcs.c | 115 + lib/jansson/test/suites/api/test_number.c | 123 + lib/jansson/test/suites/api/test_object.c | 797 +++ lib/jansson/test/suites/api/test_pack.c | 547 ++ lib/jansson/test/suites/api/test_simple.c | 287 + lib/jansson/test/suites/api/test_sprintf.c | 29 + lib/jansson/test/suites/api/test_unpack.c | 431 ++ lib/jansson/test/suites/api/test_version.c | 61 + lib/jansson/test/suites/api/util.h | 93 + .../test/suites/encoding-flags/array/input | 1 + .../test/suites/encoding-flags/array/output | 1 + .../suites/encoding-flags/compact-array/env | 1 + .../suites/encoding-flags/compact-array/input | 1 + .../encoding-flags/compact-array/output | 1 + .../suites/encoding-flags/compact-object/env | 2 + .../encoding-flags/compact-object/input | 1 + .../encoding-flags/compact-object/output | 1 + .../suites/encoding-flags/ensure-ascii/env | 1 + .../suites/encoding-flags/ensure-ascii/input | 8 + .../suites/encoding-flags/ensure-ascii/output | 1 + .../suites/encoding-flags/indent-array/env | 1 + .../suites/encoding-flags/indent-array/input | 1 + .../suites/encoding-flags/indent-array/output | 4 + .../encoding-flags/indent-compact-array/env | 2 + .../encoding-flags/indent-compact-array/input | 1 + .../indent-compact-array/output | 4 + .../encoding-flags/indent-compact-object/env | 3 + .../indent-compact-object/input | 1 + .../indent-compact-object/output | 4 + .../suites/encoding-flags/indent-object/env | 2 + .../suites/encoding-flags/indent-object/input | 1 + .../encoding-flags/indent-object/output | 4 + .../test/suites/encoding-flags/object/env | 1 + .../test/suites/encoding-flags/object/input | 1 + .../test/suites/encoding-flags/object/output | 1 + .../suites/encoding-flags/preserve-order/env | 1 + .../encoding-flags/preserve-order/input | 1 + .../encoding-flags/preserve-order/output | 1 + .../suites/encoding-flags/real-precision/env | 1 + .../encoding-flags/real-precision/input | 1 + .../encoding-flags/real-precision/output | 1 + lib/jansson/test/suites/encoding-flags/run | 22 + .../test/suites/encoding-flags/sort-keys/env | 1 + .../suites/encoding-flags/sort-keys/input | 1 + .../suites/encoding-flags/sort-keys/output | 1 + .../encoded-surrogate-half/error | 2 + .../encoded-surrogate-half/input | 1 + .../invalid-utf-8-after-backslash/error | 2 + .../invalid-utf-8-after-backslash/input | 1 + .../invalid-utf-8-in-array/error | 2 + .../invalid-utf-8-in-array/input | 1 + .../invalid-utf-8-in-bigger-int/error | 2 + .../invalid-utf-8-in-bigger-int/input | 1 + .../invalid-utf-8-in-escape/error | 2 + .../invalid-utf-8-in-escape/input | 1 + .../invalid-utf-8-in-exponent/error | 2 + .../invalid-utf-8-in-exponent/input | 1 + .../invalid-utf-8-in-identifier/error | 2 + .../invalid-utf-8-in-identifier/input | 1 + .../invalid-utf-8-in-int/error | 2 + .../invalid-utf-8-in-int/input | 1 + .../invalid-utf-8-in-real-after-e/error | 2 + .../invalid-utf-8-in-real-after-e/input | 1 + .../invalid-utf-8-in-string/error | 2 + .../invalid-utf-8-in-string/input | 1 + .../invalid-unicode/lone-invalid-utf-8/error | 2 + .../invalid-unicode/lone-invalid-utf-8/input | 1 + .../lone-utf-8-continuation-byte/error | 2 + .../lone-utf-8-continuation-byte/input | 1 + .../not-in-unicode-range/error | 2 + .../not-in-unicode-range/input | 1 + .../overlong-3-byte-encoding/error | 2 + .../overlong-3-byte-encoding/input | 1 + .../overlong-4-byte-encoding/error | 2 + .../overlong-4-byte-encoding/input | 1 + .../overlong-ascii-encoding/error | 2 + .../overlong-ascii-encoding/input | 1 + .../invalid-unicode/restricted-utf-8/error | 2 + .../invalid-unicode/restricted-utf-8/input | 1 + lib/jansson/test/suites/invalid-unicode/run | 22 + .../invalid-unicode/truncated-utf-8/error | 2 + .../invalid-unicode/truncated-utf-8/input | 1 + .../test/suites/invalid/apostrophe/error | 2 + .../test/suites/invalid/apostrophe/input | 1 + .../invalid/ascii-unicode-identifier/error | 2 + .../invalid/ascii-unicode-identifier/input | 1 + .../test/suites/invalid/brace-comma/error | 2 + .../test/suites/invalid/brace-comma/input | 1 + .../test/suites/invalid/bracket-comma/error | 2 + .../test/suites/invalid/bracket-comma/input | 1 + .../invalid/bracket-one-comma/error.normal | 2 + .../invalid/bracket-one-comma/error.strip | 2 + .../suites/invalid/bracket-one-comma/input | 1 + lib/jansson/test/suites/invalid/empty/error | 2 + lib/jansson/test/suites/invalid/empty/input | 0 .../suites/invalid/extra-comma-in-array/error | 2 + .../suites/invalid/extra-comma-in-array/input | 1 + .../extra-comma-in-multiline-array/error | 2 + .../extra-comma-in-multiline-array/input | 6 + .../invalid/garbage-after-newline/error | 2 + .../invalid/garbage-after-newline/input | 2 + .../suites/invalid/garbage-at-the-end/error | 2 + .../suites/invalid/garbage-at-the-end/input | 1 + .../invalid/integer-starting-with-zero/error | 2 + .../invalid/integer-starting-with-zero/input | 1 + .../test/suites/invalid/invalid-escape/error | 2 + .../test/suites/invalid/invalid-escape/input | 1 + .../suites/invalid/invalid-identifier/error | 2 + .../suites/invalid/invalid-identifier/input | 1 + .../invalid/invalid-negative-integer/error | 2 + .../invalid/invalid-negative-integer/input | 1 + .../invalid/invalid-negative-real/error | 2 + .../invalid/invalid-negative-real/input | 1 + .../invalid/invalid-second-surrogate/error | 2 + .../invalid/invalid-second-surrogate/input | 1 + .../invalid/invalid-unicode-escape/error | 2 + .../invalid/invalid-unicode-escape/input | 1 + .../invalid/lone-open-brace/error.normal | 2 + .../invalid/lone-open-brace/error.strip | 2 + .../test/suites/invalid/lone-open-brace/input | 1 + .../invalid/lone-open-bracket/error.normal | 2 + .../invalid/lone-open-bracket/error.strip | 2 + .../suites/invalid/lone-open-bracket/input | 1 + .../invalid/lone-second-surrogate/error | 2 + .../invalid/lone-second-surrogate/input | 1 + .../invalid/minus-sign-without-number/error | 2 + .../invalid/minus-sign-without-number/input | 1 + .../negative-integer-starting-with-zero/error | 2 + .../negative-integer-starting-with-zero/input | 1 + .../invalid/null-byte-in-object-key/error | 2 + .../invalid/null-byte-in-object-key/input | 1 + .../suites/invalid/null-byte-in-string/error | 2 + .../suites/invalid/null-byte-in-string/input | Bin 0 -> 28 bytes .../invalid/null-byte-in-string/nostrip | 2 + .../invalid/null-byte-outside-string/error | 2 + .../invalid/null-byte-outside-string/input | Bin 0 -> 3 bytes .../invalid/null-byte-outside-string/nostrip | 2 + .../invalid/null-escape-in-string/error | 2 + .../invalid/null-escape-in-string/input | 1 + lib/jansson/test/suites/invalid/null/error | 2 + lib/jansson/test/suites/invalid/null/input | 1 + .../suites/invalid/object-apostrophes/error | 2 + .../suites/invalid/object-apostrophes/input | 1 + .../invalid/object-garbage-at-end/error | 2 + .../invalid/object-garbage-at-end/input | 1 + .../object-in-unterminated-array/error.normal | 2 + .../object-in-unterminated-array/error.strip | 2 + .../object-in-unterminated-array/input | 1 + .../invalid/object-no-colon/error.normal | 2 + .../invalid/object-no-colon/error.strip | 2 + .../test/suites/invalid/object-no-colon/input | 1 + .../invalid/object-no-value/error.normal | 2 + .../invalid/object-no-value/error.strip | 2 + .../test/suites/invalid/object-no-value/input | 1 + .../object-unterminated-value/error.normal | 2 + .../object-unterminated-value/error.strip | 2 + .../invalid/object-unterminated-value/input | 1 + .../suites/invalid/real-garbage-after-e/error | 2 + .../suites/invalid/real-garbage-after-e/input | 1 + .../invalid/real-negative-overflow/error | 2 + .../invalid/real-negative-overflow/input | 1 + .../invalid/real-positive-overflow/error | 2 + .../invalid/real-positive-overflow/input | 1 + .../suites/invalid/real-truncated-at-e/error | 2 + .../suites/invalid/real-truncated-at-e/input | 1 + .../invalid/real-truncated-at-point/error | 2 + .../invalid/real-truncated-at-point/input | 1 + .../test/suites/invalid/recursion-depth/error | 2 + .../test/suites/invalid/recursion-depth/input | 1 + lib/jansson/test/suites/invalid/run | 44 + .../invalid/tab-character-in-string/error | 2 + .../invalid/tab-character-in-string/input | 1 + .../invalid/too-big-negative-integer/error | 2 + .../invalid/too-big-negative-integer/input | 1 + .../invalid/too-big-positive-integer/error | 2 + .../invalid/too-big-positive-integer/input | 1 + .../invalid/truncated-unicode-surrogate/error | 2 + .../invalid/truncated-unicode-surrogate/input | 1 + .../suites/invalid/unicode-identifier/error | 2 + .../suites/invalid/unicode-identifier/input | 1 + .../error.normal | 2 + .../unterminated-array-and-object/error.strip | 2 + .../unterminated-array-and-object/input | 1 + .../invalid/unterminated-array/error.normal | 2 + .../invalid/unterminated-array/error.strip | 2 + .../suites/invalid/unterminated-array/input | 1 + .../unterminated-empty-key/error.normal | 2 + .../unterminated-empty-key/error.strip | 2 + .../invalid/unterminated-empty-key/input | 1 + .../invalid/unterminated-key/error.normal | 2 + .../invalid/unterminated-key/error.strip | 2 + .../suites/invalid/unterminated-key/input | 1 + .../unterminated-object-and-array/error | 2 + .../unterminated-object-and-array/input | 1 + .../invalid/unterminated-string/error.normal | 2 + .../invalid/unterminated-string/error.strip | 2 + .../suites/invalid/unterminated-string/input | 1 + .../test/suites/valid/complex-array/env | 1 + .../test/suites/valid/complex-array/input | 5 + .../test/suites/valid/complex-array/output | 1 + .../test/suites/valid/empty-array/input | 1 + .../test/suites/valid/empty-array/output | 1 + .../suites/valid/empty-object-in-array/input | 1 + .../suites/valid/empty-object-in-array/output | 1 + .../test/suites/valid/empty-object/input | 1 + .../test/suites/valid/empty-object/output | 1 + .../test/suites/valid/empty-string/input | 1 + .../test/suites/valid/empty-string/output | 1 + .../valid/escaped-utf-control-char/input | 1 + .../valid/escaped-utf-control-char/output | 1 + lib/jansson/test/suites/valid/false/input | 1 + lib/jansson/test/suites/valid/false/output | 1 + .../test/suites/valid/negative-int/input | 1 + .../test/suites/valid/negative-int/output | 1 + .../test/suites/valid/negative-one/input | 1 + .../test/suites/valid/negative-one/output | 1 + .../test/suites/valid/negative-zero/input | 1 + .../test/suites/valid/negative-zero/output | 1 + lib/jansson/test/suites/valid/null/input | 1 + lib/jansson/test/suites/valid/null/output | 1 + .../test/suites/valid/one-byte-utf-8/input | 1 + .../test/suites/valid/one-byte-utf-8/output | 1 + .../real-capital-e-negative-exponent/input | 1 + .../real-capital-e-negative-exponent/output | 1 + .../real-capital-e-positive-exponent/input | 1 + .../real-capital-e-positive-exponent/output | 1 + .../test/suites/valid/real-capital-e/input | 1 + .../test/suites/valid/real-capital-e/output | 1 + .../suites/valid/real-exponent-no-dtoa/input | 1 + .../suites/valid/real-exponent-no-dtoa/output | 1 + .../valid/real-exponent-no-dtoa/skip_if_dtoa | 0 .../test/suites/valid/real-exponent/input | 1 + .../test/suites/valid/real-exponent/output | 1 + .../valid/real-exponent/skip_unless_dtoa | 0 .../suites/valid/real-fraction-exponent/input | 1 + .../valid/real-fraction-exponent/output | 1 + .../suites/valid/real-negative-exponent/input | 1 + .../valid/real-negative-exponent/output | 1 + .../suites/valid/real-positive-exponent/input | 1 + .../valid/real-positive-exponent/output | 1 + .../suites/valid/real-subnormal-number/input | 1 + .../suites/valid/real-subnormal-number/output | 1 + .../test/suites/valid/real-underflow/input | 1 + .../test/suites/valid/real-underflow/output | 1 + lib/jansson/test/suites/valid/run | 51 + .../test/suites/valid/short-string/input | 1 + .../test/suites/valid/short-string/output | 1 + .../suites/valid/simple-ascii-string/input | 1 + .../suites/valid/simple-ascii-string/output | 1 + .../test/suites/valid/simple-int-0/input | 1 + .../test/suites/valid/simple-int-0/output | 1 + .../test/suites/valid/simple-int-1/input | 1 + .../test/suites/valid/simple-int-1/output | 1 + .../test/suites/valid/simple-int-123/input | 1 + .../test/suites/valid/simple-int-123/output | 1 + .../test/suites/valid/simple-object/input | 1 + .../test/suites/valid/simple-object/output | 1 + .../test/suites/valid/simple-real/input | 1 + .../test/suites/valid/simple-real/output | 1 + .../test/suites/valid/string-escapes/input | 1 + .../test/suites/valid/string-escapes/output | 1 + .../test/suites/valid/three-byte-utf-8/input | 1 + .../test/suites/valid/three-byte-utf-8/output | 1 + lib/jansson/test/suites/valid/true/input | 1 + lib/jansson/test/suites/valid/true/output | 1 + .../test/suites/valid/two-byte-utf-8/input | 1 + .../test/suites/valid/two-byte-utf-8/output | 1 + .../test/suites/valid/utf-8-string/input | 1 + .../test/suites/valid/utf-8-string/output | 1 + .../utf-surrogate-four-byte-encoding/input | 1 + .../utf-surrogate-four-byte-encoding/output | 1 + lib/janssonpath/.gitignore | 3 + lib/{jmespath => janssonpath}/LICENSE | 12 +- lib/janssonpath/Makefile | 17 + lib/janssonpath/README.md | 74 + lib/janssonpath/janssonpath.h | 18 + lib/janssonpath/janssonpath_impl.c | 840 +++ lib/janssonpath/parse.c | 88 + lib/janssonpath/parse.h | 15 + lib/janssonpath/test/Makefile | 9 + lib/janssonpath/test/test.c | 19 + lib/janssonpath/test/test.json | 37 + lib/jmespath/.gitignore | 3 - lib/jmespath/CHANGELOG.md | 23 - lib/jmespath/README.md | 42 - lib/jmespath/impl/Cargo.lock | 198 - lib/jmespath/impl/Cargo.toml | 12 - lib/jmespath/impl/src/lib.rs | 27 - lib/jmespath/jmespath/Cargo.toml | 39 - lib/jmespath/jmespath/benches/generated.rs | 10 - lib/jmespath/jmespath/build.rs | 255 - lib/jmespath/jmespath/src/ast.rs | 231 - lib/jmespath/jmespath/src/errors.rs | 319 - lib/jmespath/jmespath/src/functions.rs | 884 --- lib/jmespath/jmespath/src/interpreter.rs | 185 - lib/jmespath/jmespath/src/lexer.rs | 537 -- lib/jmespath/jmespath/src/lib.rs | 511 -- lib/jmespath/jmespath/src/parser.rs | 473 -- lib/jmespath/jmespath/src/runtime.rs | 88 - lib/jmespath/jmespath/src/variable.rs | 1654 ----- lib/jmespath/jmespath/tests/compliance.rs | 328 - .../jmespath/tests/compliance/basic.json | 96 - .../jmespath/tests/compliance/benchmarks.json | 124 - .../jmespath/tests/compliance/boolean.json | 257 - .../jmespath/tests/compliance/current.json | 25 - .../jmespath/tests/compliance/escape.json | 46 - .../jmespath/tests/compliance/filters.json | 468 -- .../jmespath/tests/compliance/functions.json | 825 --- .../tests/compliance/identifiers.json | 1377 ---- .../jmespath/tests/compliance/indices.json | 346 - .../jmespath/tests/compliance/literal.json | 200 - .../tests/compliance/multiselect.json | 398 -- .../jmespath/tests/compliance/pipe.json | 131 - .../jmespath/tests/compliance/slice.json | 187 - .../jmespath/tests/compliance/syntax.json | 670 -- .../jmespath/tests/compliance/unicode.json | 38 - .../jmespath/tests/compliance/wildcard.json | 460 -- shogdb/include/jansson.h | 419 ++ shogdb/include/jansson_config.h | 72 + shogdb/include/janssonpath.h | 18 + shogdb/makefile | 12 +- shogdb/src/cli/cli.c | 44 +- shogdb/src/client/client.c | 66 +- shogdb/src/client/client.h | 2 + shogdb/src/db/db.c | 58 +- shogdb/src/db/db.h | 3 +- shogdb/src/db/dht.c | 816 +-- shogdb/src/db/jmespath.cpp | 12 - shogdb/src/db/json.c | 31 +- shogdb/src/db/json.h | 4 + shogdb/src/db/pins.c | 361 +- 419 files changed, 27451 insertions(+), 12159 deletions(-) create mode 100644 lib/jansson/.gitignore create mode 100644 lib/jansson/Android.mk create mode 100644 lib/jansson/CHANGES create mode 100644 lib/jansson/CMakeLists.txt create mode 100644 lib/jansson/CONTRIBUTING.md create mode 100644 lib/jansson/CleanSpec.mk create mode 100644 lib/jansson/LICENSE create mode 100644 lib/jansson/Makefile.am create mode 100644 lib/jansson/README.rst create mode 100644 lib/jansson/SECURITY.md create mode 100644 lib/jansson/android/jansson_config.h create mode 100644 lib/jansson/cmake/CheckFunctionKeywords.cmake create mode 100644 lib/jansson/cmake/CodeCoverage.cmake create mode 100644 lib/jansson/cmake/FindSphinx.cmake create mode 100644 lib/jansson/cmake/janssonConfig.cmake.in create mode 100644 lib/jansson/cmake/jansson_config.h.cmake create mode 100644 lib/jansson/cmake/jansson_private_config.h.cmake create mode 100644 lib/jansson/configure.ac create mode 100644 lib/jansson/doc/.gitignore create mode 100644 lib/jansson/doc/Makefile.am create mode 100644 lib/jansson/doc/README create mode 100644 lib/jansson/doc/apiref.rst create mode 100644 lib/jansson/doc/changes.rst create mode 100644 lib/jansson/doc/conf.py create mode 100644 lib/jansson/doc/conformance.rst create mode 100644 lib/jansson/doc/ext/refcounting.py create mode 100644 lib/jansson/doc/gettingstarted.rst create mode 100644 lib/jansson/doc/github_commits.c create mode 100644 lib/jansson/doc/index.rst create mode 100644 lib/jansson/doc/threadsafety.rst create mode 100644 lib/jansson/doc/tutorial.rst create mode 100644 lib/jansson/doc/upgrading.rst create mode 100644 lib/jansson/examples/README.rst create mode 100644 lib/jansson/examples/simple_parse.c create mode 100644 lib/jansson/jansson.pc.in create mode 100755 lib/jansson/release.sh create mode 100755 lib/jansson/scripts/clang-format create mode 100755 lib/jansson/scripts/clang-format-check create mode 100644 lib/jansson/src/Makefile.am create mode 100644 lib/jansson/src/dtoa.c create mode 100644 lib/jansson/src/dump.c create mode 100644 lib/jansson/src/error.c create mode 100644 lib/jansson/src/hashtable.c create mode 100644 lib/jansson/src/hashtable.h create mode 100644 lib/jansson/src/hashtable_seed.c create mode 100644 lib/jansson/src/jansson.def create mode 100644 lib/jansson/src/jansson.h create mode 100644 lib/jansson/src/jansson_config.h.in create mode 100644 lib/jansson/src/jansson_private.h create mode 100644 lib/jansson/src/load.c create mode 100644 lib/jansson/src/lookup3.h create mode 100644 lib/jansson/src/memory.c create mode 100644 lib/jansson/src/pack_unpack.c create mode 100644 lib/jansson/src/strbuffer.c create mode 100644 lib/jansson/src/strbuffer.h create mode 100644 lib/jansson/src/strconv.c create mode 100644 lib/jansson/src/utf.c create mode 100644 lib/jansson/src/utf.h create mode 100644 lib/jansson/src/value.c create mode 100644 lib/jansson/src/version.c create mode 100644 lib/jansson/test/.gitignore create mode 100644 lib/jansson/test/Makefile.am create mode 100644 lib/jansson/test/bin/Makefile.am create mode 100644 lib/jansson/test/bin/json_process.c create mode 100644 lib/jansson/test/ossfuzz/.gitignore create mode 100644 lib/jansson/test/ossfuzz/Makefile.am create mode 100644 lib/jansson/test/ossfuzz/json_load_dump_fuzzer.cc create mode 100755 lib/jansson/test/ossfuzz/ossfuzz.sh create mode 100644 lib/jansson/test/ossfuzz/standaloneengine.cc create mode 100644 lib/jansson/test/ossfuzz/testinput.h create mode 100755 lib/jansson/test/run-suites create mode 100644 lib/jansson/test/scripts/run-tests.sh create mode 100644 lib/jansson/test/scripts/valgrind.sh create mode 100644 lib/jansson/test/suites/.gitattributes create mode 100644 lib/jansson/test/suites/Makefile.am create mode 100644 lib/jansson/test/suites/api/Makefile.am create mode 100755 lib/jansson/test/suites/api/check-exports create mode 100755 lib/jansson/test/suites/api/run create mode 100644 lib/jansson/test/suites/api/test_array.c create mode 100644 lib/jansson/test/suites/api/test_chaos.c create mode 100644 lib/jansson/test/suites/api/test_copy.c create mode 100644 lib/jansson/test/suites/api/test_dump.c create mode 100644 lib/jansson/test/suites/api/test_dump_callback.c create mode 100644 lib/jansson/test/suites/api/test_equal.c create mode 100644 lib/jansson/test/suites/api/test_fixed_size.c create mode 100644 lib/jansson/test/suites/api/test_load.c create mode 100644 lib/jansson/test/suites/api/test_load_callback.c create mode 100644 lib/jansson/test/suites/api/test_loadb.c create mode 100644 lib/jansson/test/suites/api/test_memory_funcs.c create mode 100644 lib/jansson/test/suites/api/test_number.c create mode 100644 lib/jansson/test/suites/api/test_object.c create mode 100644 lib/jansson/test/suites/api/test_pack.c create mode 100644 lib/jansson/test/suites/api/test_simple.c create mode 100644 lib/jansson/test/suites/api/test_sprintf.c create mode 100644 lib/jansson/test/suites/api/test_unpack.c create mode 100644 lib/jansson/test/suites/api/test_version.c create mode 100644 lib/jansson/test/suites/api/util.h create mode 100644 lib/jansson/test/suites/encoding-flags/array/input create mode 100644 lib/jansson/test/suites/encoding-flags/array/output create mode 100644 lib/jansson/test/suites/encoding-flags/compact-array/env create mode 100644 lib/jansson/test/suites/encoding-flags/compact-array/input create mode 100644 lib/jansson/test/suites/encoding-flags/compact-array/output create mode 100644 lib/jansson/test/suites/encoding-flags/compact-object/env create mode 100644 lib/jansson/test/suites/encoding-flags/compact-object/input create mode 100644 lib/jansson/test/suites/encoding-flags/compact-object/output create mode 100644 lib/jansson/test/suites/encoding-flags/ensure-ascii/env create mode 100644 lib/jansson/test/suites/encoding-flags/ensure-ascii/input create mode 100644 lib/jansson/test/suites/encoding-flags/ensure-ascii/output create mode 100644 lib/jansson/test/suites/encoding-flags/indent-array/env create mode 100644 lib/jansson/test/suites/encoding-flags/indent-array/input create mode 100644 lib/jansson/test/suites/encoding-flags/indent-array/output create mode 100644 lib/jansson/test/suites/encoding-flags/indent-compact-array/env create mode 100644 lib/jansson/test/suites/encoding-flags/indent-compact-array/input create mode 100644 lib/jansson/test/suites/encoding-flags/indent-compact-array/output create mode 100644 lib/jansson/test/suites/encoding-flags/indent-compact-object/env create mode 100644 lib/jansson/test/suites/encoding-flags/indent-compact-object/input create mode 100644 lib/jansson/test/suites/encoding-flags/indent-compact-object/output create mode 100644 lib/jansson/test/suites/encoding-flags/indent-object/env create mode 100644 lib/jansson/test/suites/encoding-flags/indent-object/input create mode 100644 lib/jansson/test/suites/encoding-flags/indent-object/output create mode 100644 lib/jansson/test/suites/encoding-flags/object/env create mode 100644 lib/jansson/test/suites/encoding-flags/object/input create mode 100644 lib/jansson/test/suites/encoding-flags/object/output create mode 100644 lib/jansson/test/suites/encoding-flags/preserve-order/env create mode 100644 lib/jansson/test/suites/encoding-flags/preserve-order/input create mode 100644 lib/jansson/test/suites/encoding-flags/preserve-order/output create mode 100644 lib/jansson/test/suites/encoding-flags/real-precision/env create mode 100644 lib/jansson/test/suites/encoding-flags/real-precision/input create mode 100644 lib/jansson/test/suites/encoding-flags/real-precision/output create mode 100755 lib/jansson/test/suites/encoding-flags/run create mode 100644 lib/jansson/test/suites/encoding-flags/sort-keys/env create mode 100644 lib/jansson/test/suites/encoding-flags/sort-keys/input create mode 100644 lib/jansson/test/suites/encoding-flags/sort-keys/output create mode 100644 lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/error create mode 100644 lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/error create mode 100644 lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/input create mode 100644 lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/error create mode 100644 lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/input create mode 100644 lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/error create mode 100644 lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/input create mode 100644 lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/error create mode 100644 lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/input create mode 100644 lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/error create mode 100644 lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/input create mode 100644 lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/error create mode 100644 lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/input create mode 100644 lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/error create mode 100644 lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/input create mode 100644 lib/jansson/test/suites/invalid-unicode/restricted-utf-8/error create mode 100644 lib/jansson/test/suites/invalid-unicode/restricted-utf-8/input create mode 100755 lib/jansson/test/suites/invalid-unicode/run create mode 100644 lib/jansson/test/suites/invalid-unicode/truncated-utf-8/error create mode 100644 lib/jansson/test/suites/invalid-unicode/truncated-utf-8/input create mode 100644 lib/jansson/test/suites/invalid/apostrophe/error create mode 100644 lib/jansson/test/suites/invalid/apostrophe/input create mode 100644 lib/jansson/test/suites/invalid/ascii-unicode-identifier/error create mode 100644 lib/jansson/test/suites/invalid/ascii-unicode-identifier/input create mode 100644 lib/jansson/test/suites/invalid/brace-comma/error create mode 100644 lib/jansson/test/suites/invalid/brace-comma/input create mode 100644 lib/jansson/test/suites/invalid/bracket-comma/error create mode 100644 lib/jansson/test/suites/invalid/bracket-comma/input create mode 100644 lib/jansson/test/suites/invalid/bracket-one-comma/error.normal create mode 100644 lib/jansson/test/suites/invalid/bracket-one-comma/error.strip create mode 100644 lib/jansson/test/suites/invalid/bracket-one-comma/input create mode 100644 lib/jansson/test/suites/invalid/empty/error create mode 100644 lib/jansson/test/suites/invalid/empty/input create mode 100644 lib/jansson/test/suites/invalid/extra-comma-in-array/error create mode 100644 lib/jansson/test/suites/invalid/extra-comma-in-array/input create mode 100644 lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/error create mode 100644 lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/input create mode 100644 lib/jansson/test/suites/invalid/garbage-after-newline/error create mode 100644 lib/jansson/test/suites/invalid/garbage-after-newline/input create mode 100644 lib/jansson/test/suites/invalid/garbage-at-the-end/error create mode 100644 lib/jansson/test/suites/invalid/garbage-at-the-end/input create mode 100644 lib/jansson/test/suites/invalid/integer-starting-with-zero/error create mode 100644 lib/jansson/test/suites/invalid/integer-starting-with-zero/input create mode 100644 lib/jansson/test/suites/invalid/invalid-escape/error create mode 100644 lib/jansson/test/suites/invalid/invalid-escape/input create mode 100644 lib/jansson/test/suites/invalid/invalid-identifier/error create mode 100644 lib/jansson/test/suites/invalid/invalid-identifier/input create mode 100644 lib/jansson/test/suites/invalid/invalid-negative-integer/error create mode 100644 lib/jansson/test/suites/invalid/invalid-negative-integer/input create mode 100644 lib/jansson/test/suites/invalid/invalid-negative-real/error create mode 100644 lib/jansson/test/suites/invalid/invalid-negative-real/input create mode 100644 lib/jansson/test/suites/invalid/invalid-second-surrogate/error create mode 100644 lib/jansson/test/suites/invalid/invalid-second-surrogate/input create mode 100644 lib/jansson/test/suites/invalid/invalid-unicode-escape/error create mode 100644 lib/jansson/test/suites/invalid/invalid-unicode-escape/input create mode 100644 lib/jansson/test/suites/invalid/lone-open-brace/error.normal create mode 100644 lib/jansson/test/suites/invalid/lone-open-brace/error.strip create mode 100644 lib/jansson/test/suites/invalid/lone-open-brace/input create mode 100644 lib/jansson/test/suites/invalid/lone-open-bracket/error.normal create mode 100644 lib/jansson/test/suites/invalid/lone-open-bracket/error.strip create mode 100644 lib/jansson/test/suites/invalid/lone-open-bracket/input create mode 100644 lib/jansson/test/suites/invalid/lone-second-surrogate/error create mode 100644 lib/jansson/test/suites/invalid/lone-second-surrogate/input create mode 100644 lib/jansson/test/suites/invalid/minus-sign-without-number/error create mode 100644 lib/jansson/test/suites/invalid/minus-sign-without-number/input create mode 100644 lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/error create mode 100644 lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/input create mode 100644 lib/jansson/test/suites/invalid/null-byte-in-object-key/error create mode 100644 lib/jansson/test/suites/invalid/null-byte-in-object-key/input create mode 100644 lib/jansson/test/suites/invalid/null-byte-in-string/error create mode 100644 lib/jansson/test/suites/invalid/null-byte-in-string/input create mode 100644 lib/jansson/test/suites/invalid/null-byte-in-string/nostrip create mode 100644 lib/jansson/test/suites/invalid/null-byte-outside-string/error create mode 100644 lib/jansson/test/suites/invalid/null-byte-outside-string/input create mode 100644 lib/jansson/test/suites/invalid/null-byte-outside-string/nostrip create mode 100644 lib/jansson/test/suites/invalid/null-escape-in-string/error create mode 100644 lib/jansson/test/suites/invalid/null-escape-in-string/input create mode 100644 lib/jansson/test/suites/invalid/null/error create mode 100644 lib/jansson/test/suites/invalid/null/input create mode 100644 lib/jansson/test/suites/invalid/object-apostrophes/error create mode 100644 lib/jansson/test/suites/invalid/object-apostrophes/input create mode 100644 lib/jansson/test/suites/invalid/object-garbage-at-end/error create mode 100644 lib/jansson/test/suites/invalid/object-garbage-at-end/input create mode 100644 lib/jansson/test/suites/invalid/object-in-unterminated-array/error.normal create mode 100644 lib/jansson/test/suites/invalid/object-in-unterminated-array/error.strip create mode 100644 lib/jansson/test/suites/invalid/object-in-unterminated-array/input create mode 100644 lib/jansson/test/suites/invalid/object-no-colon/error.normal create mode 100644 lib/jansson/test/suites/invalid/object-no-colon/error.strip create mode 100644 lib/jansson/test/suites/invalid/object-no-colon/input create mode 100644 lib/jansson/test/suites/invalid/object-no-value/error.normal create mode 100644 lib/jansson/test/suites/invalid/object-no-value/error.strip create mode 100644 lib/jansson/test/suites/invalid/object-no-value/input create mode 100644 lib/jansson/test/suites/invalid/object-unterminated-value/error.normal create mode 100644 lib/jansson/test/suites/invalid/object-unterminated-value/error.strip create mode 100644 lib/jansson/test/suites/invalid/object-unterminated-value/input create mode 100644 lib/jansson/test/suites/invalid/real-garbage-after-e/error create mode 100644 lib/jansson/test/suites/invalid/real-garbage-after-e/input create mode 100644 lib/jansson/test/suites/invalid/real-negative-overflow/error create mode 100644 lib/jansson/test/suites/invalid/real-negative-overflow/input create mode 100644 lib/jansson/test/suites/invalid/real-positive-overflow/error create mode 100644 lib/jansson/test/suites/invalid/real-positive-overflow/input create mode 100644 lib/jansson/test/suites/invalid/real-truncated-at-e/error create mode 100644 lib/jansson/test/suites/invalid/real-truncated-at-e/input create mode 100644 lib/jansson/test/suites/invalid/real-truncated-at-point/error create mode 100644 lib/jansson/test/suites/invalid/real-truncated-at-point/input create mode 100644 lib/jansson/test/suites/invalid/recursion-depth/error create mode 100644 lib/jansson/test/suites/invalid/recursion-depth/input create mode 100755 lib/jansson/test/suites/invalid/run create mode 100644 lib/jansson/test/suites/invalid/tab-character-in-string/error create mode 100644 lib/jansson/test/suites/invalid/tab-character-in-string/input create mode 100644 lib/jansson/test/suites/invalid/too-big-negative-integer/error create mode 100644 lib/jansson/test/suites/invalid/too-big-negative-integer/input create mode 100644 lib/jansson/test/suites/invalid/too-big-positive-integer/error create mode 100644 lib/jansson/test/suites/invalid/too-big-positive-integer/input create mode 100644 lib/jansson/test/suites/invalid/truncated-unicode-surrogate/error create mode 100644 lib/jansson/test/suites/invalid/truncated-unicode-surrogate/input create mode 100644 lib/jansson/test/suites/invalid/unicode-identifier/error create mode 100644 lib/jansson/test/suites/invalid/unicode-identifier/input create mode 100644 lib/jansson/test/suites/invalid/unterminated-array-and-object/error.normal create mode 100644 lib/jansson/test/suites/invalid/unterminated-array-and-object/error.strip create mode 100644 lib/jansson/test/suites/invalid/unterminated-array-and-object/input create mode 100644 lib/jansson/test/suites/invalid/unterminated-array/error.normal create mode 100644 lib/jansson/test/suites/invalid/unterminated-array/error.strip create mode 100644 lib/jansson/test/suites/invalid/unterminated-array/input create mode 100644 lib/jansson/test/suites/invalid/unterminated-empty-key/error.normal create mode 100644 lib/jansson/test/suites/invalid/unterminated-empty-key/error.strip create mode 100644 lib/jansson/test/suites/invalid/unterminated-empty-key/input create mode 100644 lib/jansson/test/suites/invalid/unterminated-key/error.normal create mode 100644 lib/jansson/test/suites/invalid/unterminated-key/error.strip create mode 100644 lib/jansson/test/suites/invalid/unterminated-key/input create mode 100644 lib/jansson/test/suites/invalid/unterminated-object-and-array/error create mode 100644 lib/jansson/test/suites/invalid/unterminated-object-and-array/input create mode 100644 lib/jansson/test/suites/invalid/unterminated-string/error.normal create mode 100644 lib/jansson/test/suites/invalid/unterminated-string/error.strip create mode 100644 lib/jansson/test/suites/invalid/unterminated-string/input create mode 100644 lib/jansson/test/suites/valid/complex-array/env create mode 100644 lib/jansson/test/suites/valid/complex-array/input create mode 100644 lib/jansson/test/suites/valid/complex-array/output create mode 100644 lib/jansson/test/suites/valid/empty-array/input create mode 100644 lib/jansson/test/suites/valid/empty-array/output create mode 100644 lib/jansson/test/suites/valid/empty-object-in-array/input create mode 100644 lib/jansson/test/suites/valid/empty-object-in-array/output create mode 100644 lib/jansson/test/suites/valid/empty-object/input create mode 100644 lib/jansson/test/suites/valid/empty-object/output create mode 100644 lib/jansson/test/suites/valid/empty-string/input create mode 100644 lib/jansson/test/suites/valid/empty-string/output create mode 100644 lib/jansson/test/suites/valid/escaped-utf-control-char/input create mode 100644 lib/jansson/test/suites/valid/escaped-utf-control-char/output create mode 100644 lib/jansson/test/suites/valid/false/input create mode 100644 lib/jansson/test/suites/valid/false/output create mode 100644 lib/jansson/test/suites/valid/negative-int/input create mode 100644 lib/jansson/test/suites/valid/negative-int/output create mode 100644 lib/jansson/test/suites/valid/negative-one/input create mode 100644 lib/jansson/test/suites/valid/negative-one/output create mode 100644 lib/jansson/test/suites/valid/negative-zero/input create mode 100644 lib/jansson/test/suites/valid/negative-zero/output create mode 100644 lib/jansson/test/suites/valid/null/input create mode 100644 lib/jansson/test/suites/valid/null/output create mode 100644 lib/jansson/test/suites/valid/one-byte-utf-8/input create mode 100644 lib/jansson/test/suites/valid/one-byte-utf-8/output create mode 100644 lib/jansson/test/suites/valid/real-capital-e-negative-exponent/input create mode 100644 lib/jansson/test/suites/valid/real-capital-e-negative-exponent/output create mode 100644 lib/jansson/test/suites/valid/real-capital-e-positive-exponent/input create mode 100644 lib/jansson/test/suites/valid/real-capital-e-positive-exponent/output create mode 100644 lib/jansson/test/suites/valid/real-capital-e/input create mode 100644 lib/jansson/test/suites/valid/real-capital-e/output create mode 100644 lib/jansson/test/suites/valid/real-exponent-no-dtoa/input create mode 100644 lib/jansson/test/suites/valid/real-exponent-no-dtoa/output create mode 100644 lib/jansson/test/suites/valid/real-exponent-no-dtoa/skip_if_dtoa create mode 100644 lib/jansson/test/suites/valid/real-exponent/input create mode 100644 lib/jansson/test/suites/valid/real-exponent/output create mode 100644 lib/jansson/test/suites/valid/real-exponent/skip_unless_dtoa create mode 100644 lib/jansson/test/suites/valid/real-fraction-exponent/input create mode 100644 lib/jansson/test/suites/valid/real-fraction-exponent/output create mode 100644 lib/jansson/test/suites/valid/real-negative-exponent/input create mode 100644 lib/jansson/test/suites/valid/real-negative-exponent/output create mode 100644 lib/jansson/test/suites/valid/real-positive-exponent/input create mode 100644 lib/jansson/test/suites/valid/real-positive-exponent/output create mode 100644 lib/jansson/test/suites/valid/real-subnormal-number/input create mode 100644 lib/jansson/test/suites/valid/real-subnormal-number/output create mode 100644 lib/jansson/test/suites/valid/real-underflow/input create mode 100644 lib/jansson/test/suites/valid/real-underflow/output create mode 100755 lib/jansson/test/suites/valid/run create mode 100644 lib/jansson/test/suites/valid/short-string/input create mode 100644 lib/jansson/test/suites/valid/short-string/output create mode 100644 lib/jansson/test/suites/valid/simple-ascii-string/input create mode 100644 lib/jansson/test/suites/valid/simple-ascii-string/output create mode 100644 lib/jansson/test/suites/valid/simple-int-0/input create mode 100644 lib/jansson/test/suites/valid/simple-int-0/output create mode 100644 lib/jansson/test/suites/valid/simple-int-1/input create mode 100644 lib/jansson/test/suites/valid/simple-int-1/output create mode 100644 lib/jansson/test/suites/valid/simple-int-123/input create mode 100644 lib/jansson/test/suites/valid/simple-int-123/output create mode 100644 lib/jansson/test/suites/valid/simple-object/input create mode 100644 lib/jansson/test/suites/valid/simple-object/output create mode 100644 lib/jansson/test/suites/valid/simple-real/input create mode 100644 lib/jansson/test/suites/valid/simple-real/output create mode 100644 lib/jansson/test/suites/valid/string-escapes/input create mode 100644 lib/jansson/test/suites/valid/string-escapes/output create mode 100644 lib/jansson/test/suites/valid/three-byte-utf-8/input create mode 100644 lib/jansson/test/suites/valid/three-byte-utf-8/output create mode 100644 lib/jansson/test/suites/valid/true/input create mode 100644 lib/jansson/test/suites/valid/true/output create mode 100644 lib/jansson/test/suites/valid/two-byte-utf-8/input create mode 100644 lib/jansson/test/suites/valid/two-byte-utf-8/output create mode 100644 lib/jansson/test/suites/valid/utf-8-string/input create mode 100644 lib/jansson/test/suites/valid/utf-8-string/output create mode 100644 lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/input create mode 100644 lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/output create mode 100644 lib/janssonpath/.gitignore rename lib/{jmespath => janssonpath}/LICENSE (88%) create mode 100644 lib/janssonpath/Makefile create mode 100644 lib/janssonpath/README.md create mode 100644 lib/janssonpath/janssonpath.h create mode 100644 lib/janssonpath/janssonpath_impl.c create mode 100644 lib/janssonpath/parse.c create mode 100644 lib/janssonpath/parse.h create mode 100644 lib/janssonpath/test/Makefile create mode 100644 lib/janssonpath/test/test.c create mode 100644 lib/janssonpath/test/test.json delete mode 100644 lib/jmespath/.gitignore delete mode 100644 lib/jmespath/CHANGELOG.md delete mode 100644 lib/jmespath/README.md delete mode 100644 lib/jmespath/impl/Cargo.lock delete mode 100644 lib/jmespath/impl/Cargo.toml delete mode 100644 lib/jmespath/impl/src/lib.rs delete mode 100644 lib/jmespath/jmespath/Cargo.toml delete mode 100644 lib/jmespath/jmespath/benches/generated.rs delete mode 100644 lib/jmespath/jmespath/build.rs delete mode 100644 lib/jmespath/jmespath/src/ast.rs delete mode 100644 lib/jmespath/jmespath/src/errors.rs delete mode 100644 lib/jmespath/jmespath/src/functions.rs delete mode 100644 lib/jmespath/jmespath/src/interpreter.rs delete mode 100644 lib/jmespath/jmespath/src/lexer.rs delete mode 100644 lib/jmespath/jmespath/src/lib.rs delete mode 100644 lib/jmespath/jmespath/src/parser.rs delete mode 100644 lib/jmespath/jmespath/src/runtime.rs delete mode 100644 lib/jmespath/jmespath/src/variable.rs delete mode 100644 lib/jmespath/jmespath/tests/compliance.rs delete mode 100644 lib/jmespath/jmespath/tests/compliance/basic.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/benchmarks.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/boolean.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/current.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/escape.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/filters.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/functions.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/identifiers.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/indices.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/literal.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/multiselect.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/pipe.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/slice.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/syntax.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/unicode.json delete mode 100644 lib/jmespath/jmespath/tests/compliance/wildcard.json create mode 100644 shogdb/include/jansson.h create mode 100644 shogdb/include/jansson_config.h create mode 100644 shogdb/include/janssonpath.h delete mode 100644 shogdb/src/db/jmespath.cpp diff --git a/lib/jansson/.gitignore b/lib/jansson/.gitignore new file mode 100644 index 0000000..0a985b4 --- /dev/null +++ b/lib/jansson/.gitignore @@ -0,0 +1,37 @@ +*~ +*.o +*.a +.libs +.deps +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing +compile +test-driver +*.lo +*.la +stamp-h1 +*.pyc +*.pc +/src/jansson_config.h +/jansson_private_config.h.in +/jansson_private_config.h +/build +*.exe +.idea +cmake-build-debug/ +*.log +*.trs \ No newline at end of file diff --git a/lib/jansson/Android.mk b/lib/jansson/Android.mk new file mode 100644 index 0000000..e3b09e7 --- /dev/null +++ b/lib/jansson/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm + +LOCAL_SRC_FILES := \ + src/dump.c \ + src/error.c \ + src/hashtable.c \ + src/hashtable_seed.c \ + src/load.c \ + src/memory.c \ + src/pack_unpack.c \ + src/strbuffer.c \ + src/strconv.c \ + src/utf.c \ + src/value.c + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/android \ + $(LOCAL_PATH)/src + +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := libc +LOCAL_CFLAGS += -O3 -DHAVE_STDINT_H=1 + +LOCAL_MODULE:= libjansson + +include $(BUILD_SHARED_LIBRARY) diff --git a/lib/jansson/CHANGES b/lib/jansson/CHANGES new file mode 100644 index 0000000..8eb7bbe --- /dev/null +++ b/lib/jansson/CHANGES @@ -0,0 +1,1008 @@ +Version 2.14.1 +============== + +Work in progress + +* Fixes: + + - Fix thread safety of encoding and decoding when `uselocale` or `newlocale` + is used to switch locales inside the threads (#674, #675, #677. Thanks to + Bruno Haible the report and help with fixing.) + + - Use David M. Gay's `dtoa()` algorithm to avoid misprinting issues of real + numbers that are not exactly representable as a `double` (#680). + + If this is not desirable, use `./configure --disable-dtoa` or `cmake + -DUSE_DTOA=OFF .` + +* Build: + + - Make test output nicer in CMake based builds (#683) + - Simplify tests (#685) + +Version 2.14 +============ + +Released 2021-09-09 + +* New Features: + + - Add `json_object_getn`, `json_object_setn`, `json_object_deln`, and the + corresponding `nocheck` functions. (#520, by Maxim Zhukov) + +* Fixes: + + - Handle `sprintf` corner cases (#537, by Tobias Stoeckmann) + +* Build: + + - Symbol versioning for all exported symbols (#540, by Simon McVittie) + - Fix compiler warnings (#555, by Kelvin Lee) + +* Documentation: + + - Small fixes (#544, #546, by @i-ky) + - Sphinx 3 compatibility (#543, by Pierce Lopez) + + +Version 2.13.1 +============== + +Released 2020-05-07 + +* Build: + + - Include `jansson_version_str()` and `jansson_version_cmp()` in + shared library. (#534) + + - Include ``scripts/`` in tarball. (#535) + + +Version 2.13 +============ + +Released 2020-05-05 + +* New Features: + + - Add `jansson_version_str()` and `jansson_version_cmp()` for runtime + version checking (#465). + + - Add `json_object_update_new()`, `json_object_update_existing_new()` + and `json_object_update_missing_new()` functions (#499). + + - Add `json_object_update_recursive()` (#505). + +* Build: + + - Add ``-Wno-format-truncation`` to suppress format truncation warnings (#489). + +* Bug fixes: + + - Remove ``strtod`` macro definition for MinGW (#498). + + - Add infinite loop check in `json_deep_copy()` (#490). + + - Add ``pipe`` macro definition for MinGW (#500). + + - Enhance ``JANSSON_ATTRS`` macro to support earlier C standard(C89) (#501). + + - Update version detection for sphinx-build (#502). + +* Documentation: + + - Fix typos (#483, #494). + + - Document that call the custom free function to free the return value + of `json_dumps()` if you have a custom malloc/free (#490). + + - Add vcpkg installation instructions (#496). + + - Document that non-blocking file descriptor is not supported on + `json_loadfd()` (#503). + + +Version 2.12 +============ + +Released 2018-11-26 + +* Bug fixes: + + - Fix error message in `json_pack()` for NULL object (#409). + + - Avoid invalid memory read in `json_pack()` (#421). + + - Call va_end after va_copy in `json_vsprintf()` (#427). + + - Improve handling of formats with '?' and '*' in `json_pack()` (#438). + + - Remove inappropriate `jsonp_free()` which caused segmentation fault in + error handling (#444). + +* Build: + + - Add function attributes for GCC and CLANG to provide warnings on improper + use of jansson routines (#404). + + - Many CMake fixes (#408, #412, #415). + + - Enable -Bsymbolic-functions linker flag whenever possible. + + - Resolve various compiler warnings (#423, #430, #435, #436). + + - Fix code coverage ignored paths (#439). + +* Other: + + - Test coverage improvements (#398, #400). + + - Add VS 2017 to appveyor, update Visual Studio documentation (#417). + + - Update copyright for 2018 (#424). + + - Update install instructions in README (#401). + +Version 2.11 +============ + +Released 2018-02-09 + +* New features: + + - Add `json_pack()` format specifiers s*, o* and O* for values that + can be omitted if null (#339). + + - Add `json_error_code()` to retrieve numeric error codes (#365, #380, + #381). + + - Enable thread safety for `json_dump()` on all systems. Enable thread + safe `json_decref()` and `json_incref()` for modern compilers (#389). + + - Add `json_sprintf()` and `json_vsprintf()` (#393). + +* Bug Fixes: + + - Fix incorrect report of success from `json_dump_file()` when an error + is returned by `fclose()` (#359). + + - Make json_equal() const-correct (#344). + + - Fix incomplete stealing of references by `json_pack()` (#374). + +* Build: + + - Work around gcc's -Wimplicit-fallthrough. + + - Fix CMake detection of ``sys/types.h`` header (#375). + + - Fix `jansson.pc` generated by CMake to be more consistent with the one + generated using GNU Autotools (#368). + +* Other: + + - Miscellaneous documentation fixes (#356, #378, #395). + + - Remove unnecessary reference actions from parsers (#377). + +Version 2.10 +============ + +Released 2017-03-02 + +* New features: + + - Add JSON_EMBED encoding flag allowing arrays and objects to be encoded + into existing streams (#329). + + - Add `json_dumpb()` function for dumping to a pre-allocated buffer (#328). + + - Add `json_dumpfd()` and `json_loadfd()` functions for dumping to streaming + file descriptors (#328). + + - Add support for parsing buffers larger than 2GB (#309). + +* Build: + + - Fix CMake build when LONG_LONG_INT is defined as "" (#321) + +* Other: + + - Internal code cleanup (#311, #314) + +Version 2.9 +=========== + +Released 2016-09-18 + +* New features: + + - Add ``json_auto_t`` to automatically decref a value that goes out + of scope. Available only on GCC and Clang. (#301) + +* Build: + + - Fix CMake build (at least on Linux) by removing conflicting + jansson_config.h from the distribution (#306) + + - Change CMake install target generation to be optional (#305) + +* Documentation: + + - Small documentation fixes. + + +Version 2.8 +=========== + +Released 2016-08-30 + +* New features: + + - Always preserve insertion order of object items. + `json_object_iter()` and friends, `json_object_foreach()` and + `json_dumps()` and friends now always work in the insertion order of + object items (#293). + + - Add `json_object_foreach_safe()` macro that allows + `json_object_del()` calls during iteration (#230). + + - Add `json_get_alloc_funcs()` to allow reading the allocation + functions set by `json_set_alloc_funcs()` (#262, #264). + + - Add `json_pack()` format specifiers s?, o? and O? for values that + can be null (#261, #270). + +* Bug fixes: + + - Fix a crash when parsing inputs consisting of very deeply nested + arrays or objects (#282, #284). + + - Never convert numbers to integers in the parser when + JSON_DECODE_INT_AS_REAL is set. This fixes error messages for + overflowing numbers when JSON_DECODE_INT_AS_REAL is set (#212). + + - Fix a use-after-free in `json_pack()` error handling. + + - Fix subnormal number parsing on mingw32. + + - Handle out-of-memory situations gracefully in the hashtable + implementation (#298). + +* Build: + + - Fix build with CMake on all versions of Visual Studio up to 2015 + (#262, #289). + + - Fix pkgconfig libdir when using CMake (#268). + + - Fix CMake config for static CRT builds on Windows (#206). + + - Fix warnings on LLVM 6.0 targeting iOS arm64 (#208). + + - Add coverlls.io support via Travis for a nice test coverage badge + (#211). + + - Don't expect ``jansson_config.h`` to be in the compiler's include + path (#209). + + - Add a build-time option to set initial hashtable size (#213). + + - Use snprintf and strncpy in place of sprintf and strcpy to silence + linker warnings on OpenBSD (#233). + +* Documentation: + + - Fix various typos in documentation, and a broken link (#258). + + - Add an example program in ``examples/`` (#214, #217). + + - Fix building of documentation man pages (#207). + + - Document the fact that copying objects doesn't preserve the + insertion order of keys (#237). + +* Tests: + + - Don't use the nonstandard __FUNCTION__ macro in tests. + + - Use expr instead of $((...)) in shell scripts for Solaris 10 + compatibility. + + - Disable Visual Studio warning C4756 when triggered deliberately in + tests (#216). + + - Other minor fixes (#221, #248). + +* Other changes: + + - List all unrecognized object keys when strict unpacking fails + (#263). + + - Alter the order of the members of the hashtable_pair struct for + easier debugging. + + - Minor performance improvement to `json_dump()` and friends (#234). + + - Minor style fixes (#255, #257). + + +Version 2.7 +=========== + +Released 2014-10-02 + +* New features: + + - `json_pack()` and friends: Add format specifiers ``s%`` and ``+%`` + for a size_t string length (#141). + + - `json_unpack()` and friends: Add format specifier ``s%`` for + unpacking the string length along with the string itself (#141). + + - Add length-aware string constructors `json_stringn()` and + `json_stringn_nocheck()`, length-aware string mutators + `json_string_setn()` and `json_string_setn_nocheck()`, and a + function for getting string's length `json_string_length()` (#141, + #143). + + - Support ``\u0000`` escapes in the decoder. The support can be + enabled by using the ``JSON_ALLOW_NUL`` decoding flag (#141). + + - Add `json_boolean_value()` as an alias for `json_is_true()` + (#146). + + - Add JSON_REAL_PRECISION encoding flag/macro for controlling real + number precision (#178). + + - Define the maximum indentation as JSON_MAX_INDENT (#191). + +* Bug fixes: + + - Some malformed ``\uNNNN`` escapes could crash the decoder with an + assertion failure. + + - Avoid integer overflows with very long strings in UTF-8 decoder and + hashtable. + + - Check for *NULL* key in `json_object_get()` and + `json_object_del()` (#151). + + - Enhance hashtable seeding on Windows (#162). + + - `json_unpack()`: Allow mixing JSON_STRICT with optional keys + (#162, #163). + + - Fix int/int32 mismatch (#142). + + - Parse subnormal numbers correctly (#202). + +* Build: + + - Remove VS2010 build files. CMake should be used on Windows instead + (#165). + + - Fix CMake build flags for MinGW (#193). + + - Add CMake config files for find_package. Rename config.h to + jansson_private_config.h (#157, #159). + + - Make Valgrind checks work with CMake (#160). + + - Fix feature checks to use correct __ATOMIC flags. + + - Fix CMake checks for uint16_t and uint8_t support (#177). + + - Make Jansson build on SmartOS/Solaris (#171). + + - Work around a GCC bug on Solaris (#175). + + - Fix autoreconf on Debian (#182). + + - Don't use GNU make specific export for global AM_CFLAGS (#203, + #204). + + - Fix building on Android using the supplied Android.mk (#166, + #174). + + - Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS (#200). + +* Documentation: + + - Document JANSSON_BUILD_SHARED_LIBS CMake option (#187). + +* Tests: + + - Close file handles correctly (#198). + +* Other changes: + + - ``\uNNNN`` escapes are now encoded in upper case for better + readability. + + - Enable usage of AddressSanitizer (#180). + + +Version 2.6 +=========== + +Released 2014-02-11 + +* Security: + + - CVE-2013-6401: The hash function used by the hashtable + implementation has been changed, and is automatically seeded with + random data when the first JSON object is created. This prevents + an attacker from causing large JSON objects with specially crafted + keys perform poorly. + +* New features: + + - `json_object_seed()`: Set the seed value of the hash function. + +* Bug fixes: + + - Include CMake specific files in the release tarball. + +* Documentation: + + - Fix tutorial source to send a User-Agent header, which is now + required by the GitHub API. + + - Set all memory to zero in secure_free() example. + + +Version 2.5 +=========== + +Released 2013-09-19 + +* New features: + + - `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and + ``+#``. + + - Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers + as real in the decoder (#123). + + - Add `json_array_foreach()`, paralleling `json_object_foreach()` + (#118). + +* Bug fixes: + + - `json_dumps()` and friends: Don't crash if json is *NULL* and + ``JSON_ENCODE_ANY`` is set. + + - Fix a theoretical integer overflow in `jsonp_strdup()`. + + - Fix `l_isxdigit()` macro (#97). + + - Fix an off-by-one error in `json_array_remove()`. + +* Build: + + - Support CMake in addition to GNU Autotools (#106, #107, #112, + #115, #120, #127). + + - Support building for Android (#109). + + - Don't use ``-Werror`` by default. + + - Support building and testing with VPATH (#93). + + - Fix compilation when ``NDEBUG`` is defined (#128) + +* Tests: + + - Fix a refleak in ``test/bin/json_process.c``. + +* Documentation: + + - Clarify the return value of `json_load_callback_t`. + + - Document how to circumvent problems with separate heaps on Windows. + + - Fix memory leaks and warnings in ``github_commits.c``. + + - Use `json_decref()` properly in tutorial. + +* Other: + + - Make it possible to forward declare ``struct json_t``. + + +Version 2.4 +=========== + +Released 2012-09-23 + +* New features: + + - Add `json_boolean()` macro that returns the JSON true or false + value based on its argument (#86). + + - Add `json_load_callback()` that calls a callback function + repeatedly to read the JSON input (#57). + + - Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of + ``/`` with ``\/``. + +* Bug fixes: + + - Check for and reject NaN and Inf values for reals. Encoding these + values resulted in invalid JSON. + + - Fix `json_real_set()` to return -1 on error. + +* Build: + + - Jansson now builds on Windows with Visual Studio 2010, and + includes solution and project files in ``win32/vs2010/`` + directory. + + - Fix build warnings (#77, #78). + + - Add ``-no-undefined`` to LDFLAGS (#90). + +* Tests: + + - Fix the symbol exports test on Linux/PPC64 (#88). + +* Documentation: + + - Fix typos (#73, #84). + + +Version 2.3.1 +============= + +Released 2012-04-20 + +* Build issues: + + - Only use ``long long`` if ``strtoll()`` is also available. + +* Documentation: + + - Fix the names of library version constants in documentation. (#52) + + - Change the tutorial to use GitHub API v3. (#65) + +* Tests: + + - Make some tests locale independent. (#51) + + - Distribute the library exports test in the tarball. + + - Make test run on shells that don't support the ``export FOO=bar`` + syntax. + + +Version 2.3 +=========== + +Released 2012-01-27 + +* New features: + + - `json_unpack()` and friends: Add support for optional object keys + with the ``{s?o}`` syntax. + + - Add `json_object_update_existing()` and + `json_object_update_missing()`, for updating only existing keys or + only adding missing keys to an object. (#37) + + - Add `json_object_foreach()` for more convenient iteration over + objects. (#45, #46) + + - When decoding JSON, write the number of bytes that were read from + input to ``error.position`` also on success. This is handy with + ``JSON_DISABLE_EOF_CHECK``. + + - Add support for decoding any JSON value, not just arrays or + objects. The support is enabled with the new ``JSON_DECODE_ANY`` + flag. Patch by Andrea Marchesini. (#4) + +* Bug fixes + + - Avoid problems with object's serial number growing too big. (#40, + #41) + + - Decoding functions now return NULL if the first argument is NULL. + Patch by Andrea Marchesini. + + - Include ``jansson_config.h.win32`` in the distribution tarball. + + - Remove ``+`` and leading zeros from exponents in the encoder. + (#39) + + - Make Jansson build and work on MinGW. (#39, #38) + +* Documentation + + - Note that the same JSON values must not be encoded in parallel by + separate threads. (#42) + + - Document MinGW support. + + +Version 2.2.1 +============= + +Released 2011-10-06 + +* Bug fixes: + + - Fix real number encoding and decoding under non-C locales. (#32) + + - Fix identifier decoding under non-UTF-8 locales. (#35) + + - `json_load_file()`: Open the input file in binary mode for maximum + compatibility. + +* Documentation: + + - Clarify the lifecycle of the result of the ``s`` fromat of + `json_unpack()`. (#31) + + - Add some portability info. (#36) + + - Little clarifications here and there. + +* Other: + + - Some style fixes, issues detected by static analyzers. + + +Version 2.2 +=========== + +Released 2011-09-03 + +* New features: + + - `json_dump_callback()`: Pass the encoder output to a callback + function in chunks. + +* Bug fixes: + + - `json_string_set()`: Check that target is a string and value is + not NULL. + +* Other: + + - Documentation typo fixes and clarifications. + + +Version 2.1 +=========== + +Released 2011-06-10 + +* New features: + + - `json_loadb()`: Decode a string with a given size, useful if the + string is not null terminated. + + - Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON + value. By default, only arrays and objects can be encoded. (#19) + + - Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding + error if any JSON object in the input contins duplicate keys. (#3) + + - Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a + valid JSON input. This allows other data after the JSON data. + +* Bug fixes: + + - Fix an additional memory leak when memory allocation fails in + `json_object_set()` and friends. + + - Clear errno before calling `strtod()` for better portability. (#27) + +* Building: + + - Avoid set-but-not-used warning/error in a test. (#20) + +* Other: + + - Minor clarifications to documentation. + + +Version 2.0.1 +============= + +Released 2011-03-31 + +* Bug fixes: + + - Replace a few `malloc()` and `free()` calls with their + counterparts that support custom memory management. + + - Fix object key hashing in json_unpack() strict checking mode. + + - Fix the parentheses in ``JANSSON_VERSION_HEX`` macro. + + - Fix `json_object_size()` return value. + + - Fix a few compilation issues. + +* Portability: + + - Enhance portability of `va_copy()`. + + - Test framework portability enhancements. + +* Documentation: + + - Distribute ``doc/upgrading.rst`` with the source tarball. + + - Build documentation in strict mode in ``make distcheck``. + + +Version 2.0 +=========== + +Released 2011-02-28 + +This release is backwards incompatible with the 1.x release series. +See the chapter "Upgrading from older versions" in documentation for +details. + +* Backwards incompatible changes: + + - Unify unsigned integer usage in the API: All occurences of + unsigned int and unsigned long have been replaced with size_t. + + - Change JSON integer's underlying type to the widest signed integer + type available, i.e. long long if it's supported, otherwise long. + Add a typedef json_int_t that defines the type. + + - Change the maximum indentation depth to 31 spaces in encoder. This + frees up bits from the flags parameter of encoding functions + `json_dumpf()`, `json_dumps()` and `json_dump_file()`. + + - For future needs, add a flags parameter to all decoding functions + `json_loadf()`, `json_loads()` and `json_load_file()`. + +* New features + + - `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON + values based on a format string. + + - `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple + value extraction and validation functionality based on a format + string. + + - Add column, position and source fields to the ``json_error_t`` + struct. + + - Enhance error reporting in the decoder. + + - ``JANSSON_VERSION`` et al.: Preprocessor constants that define the + library version. + + - `json_set_alloc_funcs()`: Set custom memory allocation functions. + +* Fix many portability issues, especially on Windows. + +* Configuration + + - Add file ``jansson_config.h`` that contains site specific + configuration. It's created automatically by the configure script, + or can be created by hand if the configure script cannot be used. + The file ``jansson_config.h.win32`` can be used without + modifications on Windows systems. + + - Add a section to documentation describing how to build Jansson on + Windows. + + - Documentation now requires Sphinx 1.0 or newer. + + +Version 1.3 +=========== + +Released 2010-06-13 + +* New functions: + + - `json_object_iter_set()`, `json_object_iter_set_new()`: Change + object contents while iterating over it. + + - `json_object_iter_at()`: Return an iterator that points to a + specific object item. + +* New encoding flags: + + - ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object + keys. + +* Bug fixes: + + - Fix an error that occured when an array or object was first + encoded as empty, then populated with some data, and then + re-encoded + + - Fix the situation like above, but when the first encoding resulted + in an error + +* Documentation: + + - Clarify the documentation on reference stealing, providing an + example usage pattern + + +Version 1.2.1 +============= + +Released 2010-04-03 + +* Bug fixes: + + - Fix reference counting on ``true``, ``false`` and ``null`` + - Estimate real number underflows in decoder with 0.0 instead of + issuing an error + +* Portability: + + - Make ``int32_t`` available on all systems + - Support compilers that don't have the ``inline`` keyword + - Require Autoconf 2.60 (for ``int32_t``) + +* Tests: + + - Print test names correctly when ``VERBOSE=1`` + - ``test/suites/api``: Fail when a test fails + - Enhance tests for iterators + - Enhance tests for decoding texts that contain null bytes + +* Documentation: + + - Don't remove ``changes.rst`` in ``make clean`` + - Add a chapter on RFC conformance + + +Version 1.2 +=========== + +Released 2010-01-21 + +* New functions: + + - `json_equal()`: Test whether two JSON values are equal + - `json_copy()` and `json_deep_copy()`: Make shallow and deep copies + of JSON values + - Add a version of all functions taking a string argument that + doesn't check for valid UTF-8: `json_string_nocheck()`, + `json_string_set_nocheck()`, `json_object_set_nocheck()`, + `json_object_set_new_nocheck()` + +* New encoding flags: + + - ``JSON_SORT_KEYS``: Sort objects by key + - ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters + - ``JSON_COMPACT``: Use a compact representation with all unneeded + whitespace stripped + +* Bug fixes: + + - Revise and unify whitespace usage in encoder: Add spaces between + array and object items, never append newline to output. + - Remove const qualifier from the ``json_t`` parameter in + `json_string_set()`, `json_integer_set()` and `json_real_set`. + - Use ``int32_t`` internally for representing Unicode code points + (int is not enough on all platforms) + +* Other changes: + + - Convert ``CHANGES`` (this file) to reStructured text and add it to + HTML documentation + - The test system has been refactored. Python is no longer required + to run the tests. + - Documentation can now be built by invoking ``make html`` + - Support for pkg-config + + +Version 1.1.3 +============= + +Released 2009-12-18 + +* Encode reals correctly, so that first encoding and then decoding a + real always produces the same value +* Don't export private symbols in ``libjansson.so`` + + +Version 1.1.2 +============= + +Released 2009-11-08 + +* Fix a bug where an error message was not produced if the input file + could not be opened in `json_load_file()` +* Fix an assertion failure in decoder caused by a minus sign without a + digit after it +* Remove an unneeded include of ``stdint.h`` in ``jansson.h`` + + +Version 1.1.1 +============= + +Released 2009-10-26 + +* All documentation files were not distributed with v1.1; build + documentation in make distcheck to prevent this in the future +* Fix v1.1 release date in ``CHANGES`` + + +Version 1.1 +=========== + +Released 2009-10-20 + +* API additions and improvements: + + - Extend array and object APIs + - Add functions to modify integer, real and string values + - Improve argument validation + - Use unsigned int instead of ``uint32_t`` for encoding flags + +* Enhance documentation + + - Add getting started guide and tutorial + - Fix some typos + - General clarifications and cleanup + +* Check for integer and real overflows and underflows in decoder +* Make singleton values thread-safe (``true``, ``false`` and ``null``) +* Enhance circular reference handling +* Don't define ``-std=c99`` in ``AM_CFLAGS`` +* Add C++ guards to ``jansson.h`` +* Minor performance and portability improvements +* Expand test coverage + + +Version 1.0.4 +============= + +Released 2009-10-11 + +* Relax Autoconf version requirement to 2.59 +* Make Jansson compile on platforms where plain ``char`` is unsigned +* Fix API tests for object + + +Version 1.0.3 +============= + +Released 2009-09-14 + +* Check for integer and real overflows and underflows in decoder +* Use the Python json module for tests, or simplejson if the json + module is not found +* Distribute changelog (this file) + + +Version 1.0.2 +============= + +Released 2009-09-08 + +* Handle EOF correctly in decoder + + +Version 1.0.1 +============= + +Released 2009-09-04 + +* Fixed broken `json_is_boolean()` + + +Version 1.0 +=========== + +Released 2009-08-25 + +* Initial release diff --git a/lib/jansson/CMakeLists.txt b/lib/jansson/CMakeLists.txt new file mode 100644 index 0000000..3394236 --- /dev/null +++ b/lib/jansson/CMakeLists.txt @@ -0,0 +1,663 @@ +cmake_minimum_required (VERSION 3.1) +project(jansson C) + +# Options +option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF) +option(USE_URANDOM "Use /dev/urandom to seed the hash function." ON) +option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." ON) +option(USE_DTOA "Use dtoa for optimal floating-point to string conversions." ON) + +if (MSVC) + # This option must match the settings used in your program, in particular if you + # are linking statically + option(JANSSON_STATIC_CRT "Link the static CRT libraries" OFF ) +endif () + +option(JANSSON_EXAMPLES "Compile example applications" ON) + +if (UNIX) + option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF) +endif () + +# Set some nicer output dirs. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) +set(JANSSON_TEMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/tmp) + +# Give the debug version a different postfix for windows, +# so both the debug and release version can be built in the +# same build-tree on Windows (MSVC). +if (WIN32 AND NOT CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX "_d") +endif() + +# This is how I thought it should go +# set (JANSSON_VERSION "2.3.1") +# set (JANSSON_SOVERSION 2) + +set(JANSSON_DISPLAY_VERSION "2.14") + +# This is what is required to match the same numbers as automake's +set(JANSSON_VERSION "4.14.0") +set(JANSSON_SOVERSION 4) + +# for CheckFunctionKeywords +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include (CheckCSourceCompiles) +include (CheckFunctionExists) +include (CheckFunctionKeywords) +include (CheckIncludeFiles) +include (CheckTypeSize) + +# suppress format-truncation warning +include (CheckCCompilerFlag) +check_c_compiler_flag(-Wno-format-truncation HAS_NO_FORMAT_TRUNCATION) +if (HAS_NO_FORMAT_TRUNCATION) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-truncation") +endif() + +if (MSVC) + # Turn off Microsofts "security" warnings. + add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" ) + + if (JANSSON_STATIC_CRT) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") + endif() +endif() + +message("C compiler: ${CMAKE_C_COMPILER_ID}") + +if (JANSSON_COVERAGE) + include(CodeCoverage) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") +endif() + +check_include_files (endian.h HAVE_ENDIAN_H) +check_include_files (fcntl.h HAVE_FCNTL_H) +check_include_files (sched.h HAVE_SCHED_H) +check_include_files (unistd.h HAVE_UNISTD_H) +check_include_files (sys/param.h HAVE_SYS_PARAM_H) +check_include_files (sys/stat.h HAVE_SYS_STAT_H) +check_include_files (sys/time.h HAVE_SYS_TIME_H) +check_include_files (sys/types.h HAVE_SYS_TYPES_H) + +check_function_exists (close HAVE_CLOSE) +check_function_exists (getpid HAVE_GETPID) +check_function_exists (gettimeofday HAVE_GETTIMEOFDAY) +check_function_exists (open HAVE_OPEN) +check_function_exists (read HAVE_READ) +check_function_exists (sched_yield HAVE_SCHED_YIELD) + +# Check for the int-type includes +check_include_files (stdint.h HAVE_STDINT_H) + +include (TestBigEndian) +TEST_BIG_ENDIAN(WORDS_BIGENDIAN) + +# Check our 64 bit integer sizes +check_type_size (__int64 __INT64) +check_type_size (int64_t INT64_T) +check_type_size ("long long" LONG_LONG_INT) + +# Check our 32 bit integer sizes +check_type_size (int32_t INT32_T) +check_type_size (__int32 __INT32) +check_type_size ("long" LONG_INT) +check_type_size ("int" INT) +if (HAVE_INT32_T) + set (JSON_INT32 int32_t) +elseif (HAVE___INT32) + set (JSON_INT32 __int32) +elseif (HAVE_LONG_INT AND (LONG_INT EQUAL 4)) + set (JSON_INT32 long) +elseif (HAVE_INT AND (INT EQUAL 4)) + set (JSON_INT32 int) +else () + message (FATAL_ERROR "Could not detect a valid 32-bit integer type") +endif () + +check_type_size ("unsigned long" UNSIGNED_LONG_INT) +check_type_size ("unsigned int" UNSIGNED_INT) +check_type_size ("unsigned short" UNSIGNED_SHORT) + +check_type_size (uint32_t UINT32_T) +check_type_size (__uint32 __UINT32) +if (HAVE_UINT32_T) + set (JSON_UINT32 uint32_t) +elseif (HAVE___UINT32) + set (JSON_UINT32 __uint32) +elseif (HAVE_UNSIGNED_LONG_INT AND (UNSIGNED_LONG_INT EQUAL 4)) + set (JSON_UINT32 "unsigned long") +elseif (HAVE_UNSIGNED_INT AND (UNSIGNED_INT EQUAL 4)) + set (JSON_UINT32 "unsigned int") +else () + message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type") +endif () + +check_type_size (uint16_t UINT16_T) +check_type_size (__uint16 __UINT16) +if (HAVE_UINT16_T) + set (JSON_UINT16 uint16_t) +elseif (HAVE___UINT16) + set (JSON_UINT16 __uint16) +elseif (HAVE_UNSIGNED_INT AND (UNSIGNED_INT EQUAL 2)) + set (JSON_UINT16 "unsigned int") +elseif (HAVE_UNSIGNED_SHORT AND (UNSIGNED_SHORT EQUAL 2)) + set (JSON_UINT16 "unsigned short") +else () + message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type") +endif () + +check_type_size (uint8_t UINT8_T) +check_type_size (__uint8 __UINT8) +if (HAVE_UINT8_T) + set (JSON_UINT8 uint8_t) +elseif (HAVE___UINT8) + set (JSON_UINT8 __uint8) +else () + set (JSON_UINT8 "unsigned char") +endif () + +# Check for ssize_t and SSIZE_T existence. +check_type_size(ssize_t SSIZE_T) +check_type_size(SSIZE_T UPPERCASE_SSIZE_T) +if(NOT HAVE_SSIZE_T) + if(HAVE_UPPERCASE_SSIZE_T) + set(JSON_SSIZE SSIZE_T) + else() + set(JSON_SSIZE int) + endif() +endif() +set(CMAKE_EXTRA_INCLUDE_FILES "") + +# Check for all the variants of strtoll +check_function_exists (strtoll HAVE_STRTOLL) +check_function_exists (strtoq HAVE_STRTOQ) +check_function_exists (_strtoi64 HAVE__STRTOI64) + +# Figure out what variant we should use +if (HAVE_STRTOLL) + set (JSON_STRTOINT strtoll) +elseif (HAVE_STRTOQ) + set (JSON_STRTOINT strtoq) +elseif (HAVE__STRTOI64) + set (JSON_STRTOINT _strtoi64) +else () + # fallback to strtol (32 bit) + # this will set all the required variables + set (JSON_STRTOINT strtol) + set (JSON_INT_T long) + set (JSON_INTEGER_FORMAT "\"ld\"") +endif () + +# if we haven't defined JSON_INT_T, then we have a 64 bit conversion function. +# detect what to use for the 64 bit type. +# Note: I will prefer long long if I can get it, as that is what the automake system aimed for. +if (NOT DEFINED JSON_INT_T) + set (JSON_INTEGER_IS_LONG_LONG 1) + + if (HAVE_LONG_LONG_INT AND (LONG_LONG_INT EQUAL 8)) + set (JSON_INT_T "long long") + elseif (HAVE_INT64_T) + set (JSON_INT_T int64_t) + elseif (HAVE___INT64) + set (JSON_INT_T __int64) + else () + message (FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent") + endif () + + # Apparently, Borland BCC and MSVC wants I64d, + # Borland BCC could also accept LD + # and gcc wants ldd, + # I am not sure what cygwin will want, so I will assume I64d + + if (WIN32) # matches both msvc and cygwin + set (JSON_INTEGER_FORMAT "\"I64d\"") + else () + set (JSON_INTEGER_FORMAT "\"lld\"") + endif () +endif () + +check_include_files (locale.h HAVE_LOCALE_H) +check_function_exists(setlocale HAVE_SETLOCALE) + +# Check what the inline keyword is. +# Note that the original JSON_INLINE was always set to just 'inline', so this goes further. +check_function_keywords("inline") +check_function_keywords("__inline") +check_function_keywords("__inline__") + +if (HAVE_INLINE) + set(JSON_INLINE inline) +elseif (HAVE___INLINE) + set(JSON_INLINE __inline) +elseif (HAVE___INLINE__) + set(JSON_INLINE __inline__) +else() + # no inline on this platform + set (JSON_INLINE) +endif() + +check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1); return 0; } " HAVE_SYNC_BUILTINS) +check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE); return 0; }" HAVE_ATOMIC_BUILTINS) + +if (HAVE_SYNC_BUILTINS) + set(JSON_HAVE_SYNC_BUILTINS 1) +else() + set(JSON_HAVE_SYNC_BUILTINS 0) +endif() + +if (HAVE_ATOMIC_BUILTINS) + set(JSON_HAVE_ATOMIC_BUILTINS 1) +else() + set(JSON_HAVE_ATOMIC_BUILTINS 0) +endif() + +set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.") + +# configure the public config file +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h) + +# Copy the jansson.h file to the public include folder +file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/) + +add_definitions(-DJANSSON_USING_CMAKE) + +# configure the private config file +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_private_config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h) + +# and tell the source code to include it +add_definitions(-DHAVE_CONFIG_H) + +include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) +include_directories (${CMAKE_CURRENT_BINARY_DIR}/private_include) + +# Add the lib sources. +file(GLOB JANSSON_SRC src/*.c) +if (NOT USE_DTOA) + list(FILTER JANSSON_SRC EXCLUDE REGEX ".*dtoa\\.c$") +endif() + +set(JANSSON_HDR_PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/hashtable.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson_private.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/strbuffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/utf.h + ${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h) + +set(JANSSON_HDR_PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h) + +source_group("Library Sources" FILES ${JANSSON_SRC}) +source_group("Library Private Headers" FILES ${JANSSON_HDR_PRIVATE}) +source_group("Library Public Headers" FILES ${JANSSON_HDR_PUBLIC}) + +if(JANSSON_BUILD_SHARED_LIBS) + add_library(jansson SHARED + ${JANSSON_SRC} + ${JANSSON_HDR_PRIVATE} + ${JANSSON_HDR_PUBLIC} + src/jansson.def) + +# check if linker support --default-symver + list(APPEND CMAKE_REQUIRED_LIBRARIES "-Wl,--default-symver") + check_c_source_compiles( + " + int main (void) + { + return 0; + } + " + DSYMVER_WORKS + ) + list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "-Wl,--default-symver") + + if (SYMVER_WORKS) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--default-symver") + else() +# some linkers may only support --version-script + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/jansson.sym" "JANSSON_${JANSSON_SOVERSION} { + global: + *; +}; +") + list(APPEND CMAKE_REQUIRED_LIBRARIES "-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/jansson.sym") + check_c_source_compiles( + " + int main (void) + { + return 0; + } + " + VSCRIPT_WORKS + ) + list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/jansson.sym") + if (VSCRIPT_WORKS) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/jansson.sym") + endif() + endif() + + set_target_properties(jansson PROPERTIES + VERSION ${JANSSON_VERSION} + SOVERSION ${JANSSON_SOVERSION}) +else() + add_library(jansson STATIC + ${JANSSON_SRC} + ${JANSSON_HDR_PRIVATE} + ${JANSSON_HDR_PUBLIC}) + set_target_properties(jansson PROPERTIES + POSITION_INDEPENDENT_CODE true) +endif() + +if (JANSSON_EXAMPLES) + add_executable(simple_parse "${CMAKE_CURRENT_SOURCE_DIR}/examples/simple_parse.c") + target_link_libraries(simple_parse jansson) +endif() + +# For building Documentation (uses Sphinx) +option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." ON) +if (JANSSON_BUILD_DOCS) + find_package(Sphinx) + + if (NOT SPHINX_FOUND) + message(WARNING "Sphinx not found. Cannot generate documentation! + Set -DJANSSON_BUILD_DOCS=OFF to get rid of this message.") + else() + if (Sphinx_VERSION_STRING VERSION_LESS 1.0) + message(WARNING "Your Sphinx version is too old! + This project requires Sphinx v1.0 or above to produce + proper documentation (you have v${Sphinx_VERSION_STRING}). + You will get output but it will have errors.") + endif() + + # configured documentation tools and intermediate build results + set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build") + + # Sphinx cache with pickled ReST documents + set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees") + + # CMake could be used to build the conf.py file too, + # eg it could automatically write the version of the program or change the theme. + # if(NOT DEFINED SPHINX_THEME) + # set(SPHINX_THEME default) + # endif() + # + # if(NOT DEFINED SPHINX_THEME_DIR) + # set(SPHINX_THEME_DIR) + # endif() + # + # configure_file( + # "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" + # "${BINARY_BUILD_DIR}/conf.py" + # @ONLY) + + # TODO: Add support for all sphinx builders: http://sphinx-doc.org/builders.html + + # Add documentation targets. + set(DOC_TARGETS html) + + option(JANSSON_BUILD_MAN "Create a target for building man pages." ON) + + if (JANSSON_BUILD_MAN) + if (Sphinx_VERSION_STRING VERSION_LESS 1.0) + message(WARNING "Sphinx version 1.0 > is required to build man pages. You have v${Sphinx_VERSION_STRING}.") + else() + list(APPEND DOC_TARGETS man) + endif() + endif() + + option(JANSSON_BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF) + + if (JANSSON_BUILD_LATEX) + find_package(LATEX) + + if (NOT LATEX_COMPILER) + message("Couldn't find Latex, can't build latex docs using Sphinx") + else() + message("Latex found! If you have problems building, see Sphinx documentation for required Latex packages.") + list(APPEND DOC_TARGETS latex) + endif() + endif() + + # The doc target will build all documentation targets. + add_custom_target(doc) + + foreach (DOC_TARGET ${DOC_TARGETS}) + add_custom_target(${DOC_TARGET} + ${SPHINX_EXECUTABLE} + # -q # Enable for quiet mode + -b ${DOC_TARGET} + -d "${SPHINX_CACHE_DIR}" + # -c "${BINARY_BUILD_DIR}" # enable if using cmake-generated conf.py + "${CMAKE_CURRENT_SOURCE_DIR}/doc" + "${CMAKE_CURRENT_BINARY_DIR}/doc/${DOC_TARGET}" + COMMENT "Building ${DOC_TARGET} documentation with Sphinx") + + add_dependencies(doc ${DOC_TARGET}) + endforeach() + + message("Building documentation enabled for: ${DOC_TARGETS}") + endif() +endif () + + +option(JANSSON_WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF) + +if (NOT JANSSON_WITHOUT_TESTS) + option(JANSSON_TEST_WITH_VALGRIND "Enable valgrind tests." OFF) + + ENABLE_TESTING() + + if (JANSSON_TEST_WITH_VALGRIND) + # TODO: Add FindValgrind.cmake instead of having a hardcoded path. + + add_definitions(-DVALGRIND) + + # enable valgrind + set(CMAKE_MEMORYCHECK_COMMAND valgrind) + set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS + "--error-exitcode=1 --leak-check=full --show-reachable=yes --track-origins=yes -q") + + set(MEMCHECK_COMMAND + "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}") + separate_arguments(MEMCHECK_COMMAND) + endif () + + # + # Test suites. + # + if (CMAKE_COMPILER_IS_GNUCC) + add_definitions(-Wall -Wextra -Wdeclaration-after-statement) + endif () + + set(api_tests + test_array + test_chaos + test_copy + test_dump + test_dump_callback + test_equal + test_fixed_size + test_load + test_load_callback + test_loadb + test_number + test_object + test_pack + test_simple + test_sprintf + test_unpack) + + # Doing arithmetic on void pointers is not allowed by Microsofts compiler + # such as secure_malloc and secure_free is doing, so exclude it for now. + if (NOT MSVC) + list(APPEND api_tests test_memory_funcs) + endif() + + # Helper macro for building and linking a test program. + macro(build_testprog name dir) + add_executable(${name} ${dir}/${name}.c) + add_dependencies(${name} jansson) + target_link_libraries(${name} jansson) + endmacro(build_testprog) + + # Create executables and tests/valgrind tests for API tests. + foreach (test ${api_tests}) + build_testprog(${test} ${CMAKE_CURRENT_SOURCE_DIR}/test/suites/api) + + if (JANSSON_TEST_WITH_VALGRIND) + add_test(memcheck__${test} + ${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test} + WORKING_DIRECTORY ${JANSSON_TEMP_DIR}) + else() + add_test(${test} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test} + WORKING_DIRECTORY ${JANSSON_TEMP_DIR}) + endif () + endforeach () + + # Test harness for the suites tests. + build_testprog(json_process ${CMAKE_CURRENT_SOURCE_DIR}/test/bin) + + set(SUITE_TEST_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process) + set(SUITES encoding-flags valid invalid invalid-unicode) + foreach (SUITE ${SUITES}) + file(GLOB TESTDIRS test/suites/${SUITE}/*) + + foreach (TESTDIR ${TESTDIRS}) + if (IS_DIRECTORY ${TESTDIR}) + get_filename_component(TNAME ${TESTDIR} NAME) + + if ((USE_DTOA AND EXISTS ${TESTDIR}/skip_if_dtoa) OR + (NOT USE_DTOA AND EXISTS ${TESTDIR}/skip_unless_dtoa)) + continue() + endif() + + if (JANSSON_TEST_WITH_VALGRIND) + add_test(memcheck__${SUITE}__${TNAME} + ${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR}) + else() + add_test(${SUITE}__${TNAME} + ${SUITE_TEST_CMD} ${TESTDIR}) + endif() + + if ((${SUITE} STREQUAL "valid" OR ${SUITE} STREQUAL "invalid") AND NOT EXISTS ${TESTDIR}/nostrip) + if (JANSSON_TEST_WITH_VALGRIND) + add_test(memcheck__${SUITE}__${TNAME}__strip + ${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} --strip ${TESTDIR}) + else() + add_test(${SUITE}__${TNAME}__strip + ${SUITE_TEST_CMD} --strip ${TESTDIR}) + endif() + endif () + endif () + endforeach () + endforeach () + + if (JANSSON_COVERAGE) + SETUP_TARGET_FOR_COVERAGE(coverage coverage ctest) + endif () + + # Enable using "make check" just like the autotools project. + # By default cmake creates a target "make test" + add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} + DEPENDS json_process ${api_tests}) +endif () + +# +# Installation preparation. +# + +# Allow the user to override installation directories. +set(JANSSON_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(JANSSON_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(JANSSON_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR cmake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/cmake/jansson) +endif() + +set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") + +# Create pkg-conf file. +# (We use the same files as ./configure does, so we +# have to defined the same variables used there). +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix "\${prefix}") +set(libdir "\${exec_prefix}/${JANSSON_INSTALL_LIB_DIR}") +set(includedir "\${prefix}/${JANSSON_INSTALL_INCLUDE_DIR}") +set(VERSION ${JANSSON_DISPLAY_VERSION}) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY) + +# Make sure the paths are relative. +foreach(p LIB BIN INCLUDE CMAKE) + set(var JANSSON_INSTALL_${p}_DIR) +endforeach() + +# Generate the config file for the build-tree. +set(JANSSON__INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/include") +set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/janssonConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/janssonConfig.cmake + @ONLY) + + +# Generate the config file for the installation tree. +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfigVersion.cmake" + VERSION ${JANSSON_VERSION} + COMPATIBILITY ExactVersion +) + +configure_package_config_file( + "cmake/janssonConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfig.cmake" + INSTALL_DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" +) + +# +# Install targets. +# +option(JANSSON_INSTALL "Generate installation target" ON) +if (JANSSON_INSTALL) + install(TARGETS jansson + EXPORT janssonTargets + LIBRARY DESTINATION "lib" + ARCHIVE DESTINATION "lib" + RUNTIME DESTINATION "bin" + INCLUDES DESTINATION "include") + + install(FILES ${JANSSON_HDR_PUBLIC} + DESTINATION "include") + + # Install the pkg-config. + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/jansson.pc + DESTINATION lib/pkgconfig) + + # Install the configs. + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/cmake/janssonConfigVersion.cmake + DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}") + + # Install exports for the install-tree. + install(EXPORT janssonTargets + NAMESPACE jansson:: + DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}") +endif() + +# For use when simply using add_library from a parent project to build jansson. +set(JANSSON_LIBRARIES jansson CACHE STRING "jansson libraries") diff --git a/lib/jansson/CONTRIBUTING.md b/lib/jansson/CONTRIBUTING.md new file mode 100644 index 0000000..b515cfc --- /dev/null +++ b/lib/jansson/CONTRIBUTING.md @@ -0,0 +1,3 @@ +Hi, and thanks for contributing! + +Please remember to add tests and documentation for new functionality. Backwards incompatible changes or features that are not directly related to JSON are likely to be rejected. diff --git a/lib/jansson/CleanSpec.mk b/lib/jansson/CleanSpec.mk new file mode 100644 index 0000000..b84e1b6 --- /dev/null +++ b/lib/jansson/CleanSpec.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/lib/jansson/LICENSE b/lib/jansson/LICENSE new file mode 100644 index 0000000..418ee6a --- /dev/null +++ b/lib/jansson/LICENSE @@ -0,0 +1,43 @@ +# License + +This project is licensed under the MIT license, except where otherwise noted. +The full text of the MIT license is included below. + +## MIT License + +Copyright (c) 2009-2024 Petri Lehtinen + +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. + +## Exceptions + +### `src/dtoa.c` + +Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + +Permission to use, copy, modify, and distribute this software for any +purpose without fee is hereby granted, provided that this entire notice +is included in all copies of any software which is or includes a copy +or modification of this software and in all copies of the supporting +documentation for such software. + +THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY +REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. diff --git a/lib/jansson/Makefile.am b/lib/jansson/Makefile.am new file mode 100644 index 0000000..a6f9192 --- /dev/null +++ b/lib/jansson/Makefile.am @@ -0,0 +1,12 @@ +EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android examples scripts +SUBDIRS = doc src test + +# "make distcheck" builds the dvi target, so use it to check that the +# documentation is built correctly. +dvi: + $(MAKE) SPHINXOPTS_EXTRA=-W html + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = jansson.pc + +TESTS = scripts/clang-format-check diff --git a/lib/jansson/README.rst b/lib/jansson/README.rst new file mode 100644 index 0000000..5a77324 --- /dev/null +++ b/lib/jansson/README.rst @@ -0,0 +1,70 @@ +Jansson README +============== + +.. |tests| image:: https://github.com/akheron/jansson/workflows/tests/badge.svg +.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/lmhkkc4q8cwc65ko + +|tests| |appveyor| + +Jansson_ is a C library for encoding, decoding and manipulating JSON +data. Its main features and design principles are: + +- Simple and intuitive API and data model + +- `Comprehensive documentation`_ + +- No dependencies on other libraries + +- Full Unicode support (UTF-8) + +- Extensive test suite + +Jansson is licensed under the `MIT license`_; see LICENSE in the +source distribution for details. + +Compilation and Installation +---------------------------- + +If you obtained a ``jansson-X.Y.tar.*`` tarball from GitHub Releases, just use +the standard autotools commands:: + + $ ./configure + $ make + $ make install + +To run the test suite, invoke:: + + $ make check + +If the source has been checked out from a Git repository, the ``configure`` +script has to be generated first. The easiest way is to use autoreconf:: + + $ autoreconf -i + + +Documentation +------------- + +Documentation is available at http://jansson.readthedocs.io/en/latest/. + +The documentation source is in the ``doc/`` subdirectory. To generate +HTML documentation, invoke:: + + $ make html + +Then, point your browser to ``doc/_build/html/index.html``. Sphinx_ +1.0 or newer is required to generate the documentation. + + +Community +--------- + +* `Documentation `_ +* `Issue tracker `_ +* `Mailing list `_ +* `Wiki `_ contains some development documentation + +.. _Jansson: http://www.digip.org/jansson/ +.. _`Comprehensive documentation`: http://jansson.readthedocs.io/en/latest/ +.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php +.. _Sphinx: http://sphinx.pocoo.org/ diff --git a/lib/jansson/SECURITY.md b/lib/jansson/SECURITY.md new file mode 100644 index 0000000..255d1da --- /dev/null +++ b/lib/jansson/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Supported Versions + +Latest released version. + +## Reporting a Vulnerability + +Send an email to petri@digip.org. diff --git a/lib/jansson/android/jansson_config.h b/lib/jansson/android/jansson_config.h new file mode 100644 index 0000000..448010a --- /dev/null +++ b/lib/jansson/android/jansson_config.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE inline +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG 1 + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/lib/jansson/cmake/CheckFunctionKeywords.cmake b/lib/jansson/cmake/CheckFunctionKeywords.cmake new file mode 100644 index 0000000..44601fd --- /dev/null +++ b/lib/jansson/cmake/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/lib/jansson/cmake/CodeCoverage.cmake b/lib/jansson/cmake/CodeCoverage.cmake new file mode 100644 index 0000000..3a21d3d --- /dev/null +++ b/lib/jansson/cmake/CodeCoverage.cmake @@ -0,0 +1,163 @@ +# +# Boost Software License - Version 1.0 - August 17th, 2003 +# +# Permission is hereby granted, free of charge, to any person or organization +# obtaining a copy of the software and accompanying documentation covered by +# this license (the "Software") to use, reproduce, display, distribute, +# execute, and transmit the Software, and to prepare derivative works of the +# Software, and to permit third-parties to whom the Software is furnished to +# do so, all subject to the following: +# +# The copyright notices in the Software and this entire statement, including +# the above license grant, this restriction and the following disclaimer, +# must be included in all copies of the Software, in whole or in part, and +# all derivative works of the Software, unless such copies or derivative +# works are solely in the form of machine-executable object code generated by +# a source language processor. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# USAGE: +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt: +# INCLUDE(CodeCoverage) +# +# 3. Set compiler flags to turn off optimization and enable coverage: +# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# +# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target +# which runs your test executable and produces a lcov code coverage report: +# Example: +# SETUP_TARGET_FOR_COVERAGE( +# my_coverage_target # Name for custom target. +# test_driver # Name of the test driver executable that runs the tests. +# # NOTE! This should always have a ZERO as exit code +# # otherwise the coverage generation will not complete. +# coverage # Name of output directory. +# ) +# +# 4. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# +# + +# Check prereqs +FIND_PROGRAM( GCOV_PATH gcov ) +FIND_PROGRAM( LCOV_PATH lcov ) +FIND_PROGRAM( GENHTML_PATH genhtml ) +FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests) + +IF(NOT GCOV_PATH) + MESSAGE(FATAL_ERROR "gcov not found! Aborting...") +ENDIF() # NOT GCOV_PATH + +IF(NOT (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC)) + # Clang version 3.0.0 and greater now supports gcov as well. + MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.") + + IF(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")) + MESSAGE(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...") + ENDIF() +ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX + +IF ( NOT CMAKE_BUILD_TYPE STREQUAL "Debug" ) + MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) +ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + + +# Param _targetname The name of new the custom make target +# Param _outputname lcov output is generated as _outputname.info +# HTML report is generated in _outputname/index.html +# Param _testrunner The name of the target which runs the tests. +# MUST return ZERO always, even on errors. +# If not, no coverage report will be created! +# Optional fourth parameter is passed as arguments to _testrunner +# Pass them in list form, e.g.: "-j;2" for -j 2 +FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner) + + IF(NOT LCOV_PATH) + MESSAGE(FATAL_ERROR "lcov not found! Aborting...") + ENDIF() # NOT LCOV_PATH + + IF(NOT GENHTML_PATH) + MESSAGE(FATAL_ERROR "genhtml not found! Aborting...") + ENDIF() # NOT GENHTML_PATH + + # Setup target + ADD_CUSTOM_TARGET(${_targetname} + + # Cleanup lcov + ${LCOV_PATH} --directory . --zerocounters + + # Run tests + COMMAND ${_testrunner} ${ARGV3} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info --rc lcov_branch_coverage=1 + COMMAND ${LCOV_PATH} --remove ${_outputname}.info '*/build/include/*' '*/test/*' '/usr/include/*' --output-file ${_outputname}.info --rc lcov_branch_coverage=1 + # COMMAND ${GENHTML_PATH} --branch-coverage -o ${_outputname} ${_outputname}.info.cleaned + # COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned + + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show info where to find the report + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD + COMMAND ; + COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report." + ) + +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE + +# Param _targetname The name of new the custom make target +# Param _testrunner The name of the target which runs the tests +# Param _outputname cobertura output is generated as _outputname.xml +# Optional fourth parameter is passed as arguments to _testrunner +# Pass them in list form, e.g.: "-j;2" for -j 2 +FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname) + + IF(NOT PYTHON_EXECUTABLE) + MESSAGE(FATAL_ERROR "Python not found! Aborting...") + ENDIF() # NOT PYTHON_EXECUTABLE + + IF(NOT GCOVR_PATH) + MESSAGE(FATAL_ERROR "gcovr not found! Aborting...") + ENDIF() # NOT GCOVR_PATH + + ADD_CUSTOM_TARGET(${_targetname} + + # Run tests + ${_testrunner} ${ARGV3} + + # Running gcovr + COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${_outputname}.xml." + ) + +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA + diff --git a/lib/jansson/cmake/FindSphinx.cmake b/lib/jansson/cmake/FindSphinx.cmake new file mode 100644 index 0000000..3bf0a5d --- /dev/null +++ b/lib/jansson/cmake/FindSphinx.cmake @@ -0,0 +1,315 @@ +# +# PART B. DOWNLOADING AGREEMENT - LICENSE FROM SBIA WITH RIGHT TO SUBLICENSE ("SOFTWARE LICENSE"). +# ------------------------------------------------------------------------------------------------ +# +# 1. As used in this Software License, "you" means the individual downloading and/or +# using, reproducing, modifying, displaying and/or distributing the Software and +# the institution or entity which employs or is otherwise affiliated with such +# individual in connection therewith. The Section of Biomedical Image Analysis, +# Department of Radiology at the Universiy of Pennsylvania ("SBIA") hereby grants +# you, with right to sublicense, with respect to SBIA's rights in the software, +# and data, if any, which is the subject of this Software License (collectively, +# the "Software"), a royalty-free, non-exclusive license to use, reproduce, make +# derivative works of, display and distribute the Software, provided that: +# (a) you accept and adhere to all of the terms and conditions of this Software +# License; (b) in connection with any copy of or sublicense of all or any portion +# of the Software, all of the terms and conditions in this Software License shall +# appear in and shall apply to such copy and such sublicense, including without +# limitation all source and executable forms and on any user documentation, +# prefaced with the following words: "All or portions of this licensed product +# (such portions are the "Software") have been obtained under license from the +# Section of Biomedical Image Analysis, Department of Radiology at the University +# of Pennsylvania and are subject to the following terms and conditions:" +# (c) you preserve and maintain all applicable attributions, copyright notices +# and licenses included in or applicable to the Software; (d) modified versions +# of the Software must be clearly identified and marked as such, and must not +# be misrepresented as being the original Software; and (e) you consider making, +# but are under no obligation to make, the source code of any of your modifications +# to the Software freely available to others on an open source basis. +# +# 2. The license granted in this Software License includes without limitation the +# right to (i) incorporate the Software into proprietary programs (subject to +# any restrictions applicable to such programs), (ii) add your own copyright +# statement to your modifications of the Software, and (iii) provide additional +# or different license terms and conditions in your sublicenses of modifications +# of the Software; provided that in each case your use, reproduction or +# distribution of such modifications otherwise complies with the conditions +# stated in this Software License. +# +# 3. This Software License does not grant any rights with respect to third party +# software, except those rights that SBIA has been authorized by a third +# party to grant to you, and accordingly you are solely responsible for +# (i) obtaining any permissions from third parties that you need to use, +# reproduce, make derivative works of, display and distribute the Software, +# and (ii) informing your sublicensees, including without limitation your +# end-users, of their obligations to secure any such required permissions. +# +# 4. The Software has been designed for research purposes only and has not been +# reviewed or approved by the Food and Drug Administration or by any other +# agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL APPLICATIONS ARE NEITHER +# RECOMMENDED NOR ADVISED. Any commercialization of the Software is at the +# sole risk of the party or parties engaged in such commercialization. +# You further agree to use, reproduce, make derivative works of, display +# and distribute the Software in compliance with all applicable governmental +# laws, regulations and orders, including without limitation those relating +# to export and import control. +# +# 5. The Software is provided "AS IS" and neither SBIA nor any contributor to +# the software (each a "Contributor") shall have any obligation to provide +# maintenance, support, updates, enhancements or modifications thereto. +# SBIA AND ALL CONTRIBUTORS SPECIFICALLY DISCLAIM ALL EXPRESS AND IMPLIED +# WARRANTIES OF ANY KIND INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +# IN NO EVENT SHALL SBIA OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR +# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY RELATED +# TO THE SOFTWARE, EVEN IF SBIA OR ANY CONTRIBUTOR HAS BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM EXTENT NOT PROHIBITED BY LAW OR +# REGULATION, YOU FURTHER ASSUME ALL LIABILITY FOR YOUR USE, REPRODUCTION, +# MAKING OF DERIVATIVE WORKS, DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE +# AND AGREE TO INDEMNIFY AND HOLD HARMLESS SBIA AND ALL CONTRIBUTORS FROM +# AND AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS ARISING +# THEREFROM. +# +# 6. None of the names, logos or trademarks of SBIA or any of SBIA's affiliates +# or any of the Contributors, or any funding agency, may be used to endorse +# or promote products produced in whole or in part by operation of the Software +# or derived from or based on the Software without specific prior written +# permission from the applicable party. +# +# 7. Any use, reproduction or distribution of the Software which is not in accordance +# with this Software License shall automatically revoke all rights granted to you +# under this Software License and render Paragraphs 1 and 2 of this Software +# License null and void. +# +# 8. This Software License does not grant any rights in or to any intellectual +# property owned by SBIA or any Contributor except those rights expressly +# granted hereunder. +# +# +# PART C. MISCELLANEOUS +# --------------------- +# +# This Agreement shall be governed by and construed in accordance with the laws +# of The Commonwealth of Pennsylvania without regard to principles of conflicts +# of law. This Agreement shall supercede and replace any license terms that you +# may have agreed to previously with respect to Software from SBIA. +# +############################################################################## +# @file FindSphinx.cmake +# @brief Find Sphinx documentation build tools. +# +# @par Input variables: +# +# +# @tp @b Sphinx_DIR @endtp +# +# +# +# @tp @b SPHINX_DIR @endtp +# +# +# +# @tp @b Sphinx_FIND_COMPONENTS @endtp +# +# +#
Installation directory of Sphinx tools. Can also be set as environment variable.
Alternative environment variable for @c Sphinx_DIR.
Sphinx build tools to look for, i.e., 'apidoc' and/or 'build'.
+# +# @par Output variables: +# +# +# @tp @b Sphinx_FOUND @endtp +# +# +# +# @tp @b SPHINX_FOUND @endtp +# +# +# @tp @b SPHINX_EXECUTABLE @endtp +# +# +# +# @tp @b Sphinx_PYTHON_EXECUTABLE @endtp +# +# +# +# @tp @b Sphinx_PYTHON_OPTIONS @endtp +# +# +# +# @tp @b Sphinx-build_EXECUTABLE @endtp +# +# +# +# @tp @b Sphinx-apidoc_EXECUTABLE @endtp +# +# +# +# @tp @b Sphinx_VERSION_STRING @endtp +# +# +# +# @tp @b Sphinx_VERSION_MAJOR @endtp +# +# +# +# @tp @b Sphinx_VERSION_MINOR @endtp +# +# +# +# @tp @b Sphinx_VERSION_PATCH @endtp +# +# +#
Whether all or only the requested Sphinx build tools were found.
Alias for @c Sphinx_FOUND. +#
Non-cached alias for @c Sphinx-build_EXECUTABLE.
Python executable used to run sphinx-build. This is either the +# by default found Python interpreter or a specific version as +# specified by the shebang (#!) of the sphinx-build script.
A list of Python options extracted from the shebang (#!) of the +# sphinx-build script. The -E option is added by this module +# if the Python executable is not the system default to avoid +# problems with a differing setting of the @c PYTHONHOME.
Absolute path of the found sphinx-build tool.
Absolute path of the found sphinx-apidoc tool.
Sphinx version found e.g. 1.1.2.
Sphinx major version found e.g. 1.
Sphinx minor version found e.g. 1.
Sphinx patch version found e.g. 2.
+# +# @ingroup CMakeFindModules +############################################################################## + +set (_Sphinx_REQUIRED_VARS) + +# ---------------------------------------------------------------------------- +# initialize search +if (NOT Sphinx_DIR) + if (NOT $ENV{Sphinx_DIR} STREQUAL "") + set (Sphinx_DIR "$ENV{Sphinx_DIR}" CACHE PATH "Installation prefix of Sphinx (docutils)." FORCE) + else () + set (Sphinx_DIR "$ENV{SPHINX_DIR}" CACHE PATH "Installation prefix of Sphinx (docutils)." FORCE) + endif () +endif () + +# ---------------------------------------------------------------------------- +# default components to look for +if (NOT Sphinx_FIND_COMPONENTS) + set (Sphinx_FIND_COMPONENTS "build") +elseif (NOT Sphinx_FIND_COMPONENTS MATCHES "^(build|apidoc)$") + message (FATAL_ERROR "Invalid Sphinx component in: ${Sphinx_FIND_COMPONENTS}") +endif () + +# ---------------------------------------------------------------------------- +# find components, i.e., build tools +foreach (_Sphinx_TOOL IN LISTS Sphinx_FIND_COMPONENTS) + if (Sphinx_DIR) + find_program ( + Sphinx-${_Sphinx_TOOL}_EXECUTABLE + NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py + HINTS "${Sphinx_DIR}" + PATH_SUFFIXES bin + DOC "The sphinx-${_Sphinx_TOOL} Python script." + NO_DEFAULT_PATH + ) + else () + find_program ( + Sphinx-${_Sphinx_TOOL}_EXECUTABLE + NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py + DOC "The sphinx-${_Sphinx_TOOL} Python script." + ) + endif () + mark_as_advanced (Sphinx-${_Sphinx_TOOL}_EXECUTABLE) + list (APPEND _Sphinx_REQUIRED_VARS Sphinx-${_Sphinx_TOOL}_EXECUTABLE) +endforeach () + +# ---------------------------------------------------------------------------- +# determine Python executable used by Sphinx +if (Sphinx-build_EXECUTABLE) + # extract python executable from shebang of sphinx-build + find_package (PythonInterp QUIET) + set (Sphinx_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}") + set (Sphinx_PYTHON_OPTIONS) + file (STRINGS "${Sphinx-build_EXECUTABLE}" FIRST_LINE LIMIT_COUNT 1) + if (FIRST_LINE MATCHES "^#!(.*/python.*)") # does not match "#!/usr/bin/env python" ! + string (REGEX REPLACE "^ +| +$" "" Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}") + if (Sphinx_PYTHON_EXECUTABLE MATCHES "([^ ]+) (.*)") + set (Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}") + string (REGEX REPLACE " +" ";" Sphinx_PYTHON_OPTIONS "${CMAKE_MATCH_2}") + endif () + endif () + # this is done to avoid problems with multiple Python versions being installed + # remember: CMake command if(STR EQUAL STR) is bad and may cause many troubles ! + string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" _Sphinx_PYTHON_EXECUTABLE_RE "${PYTHON_EXECUTABLE}") + list (FIND Sphinx_PYTHON_OPTIONS -E IDX) + if (IDX EQUAL -1 AND NOT Sphinx_PYTHON_EXECUTABLE MATCHES "^${_Sphinx_PYTHON_EXECUTABLE_RE}$") + list (INSERT Sphinx_PYTHON_OPTIONS 0 -E) + endif () + unset (_Sphinx_PYTHON_EXECUTABLE_RE) +endif () + +# ---------------------------------------------------------------------------- +# determine Sphinx version +# some quick experiments by @ploxiln +# - sphinx 1.7 and later have the version output format like "sphinx-build 1.7.2" +# - sphinx 1.2 through 1.6 have the version output format like "Sphinx (sphinx-build) 1.2.2" +# - sphinx 1.1 and before do not have a "--version" flag, but it causes the help output like "-h" does which includes version like "Sphinx v1.0.2" +if (Sphinx-build_EXECUTABLE) + # intentionally use invalid -h option here as the help that is shown then + # will include the Sphinx version information + if (Sphinx_PYTHON_EXECUTABLE) + execute_process ( + COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" --version + OUTPUT_VARIABLE _Sphinx_VERSION + ERROR_VARIABLE _Sphinx_VERSION + ) + elseif (UNIX) + execute_process ( + COMMAND "${Sphinx-build_EXECUTABLE}" --version + OUTPUT_VARIABLE _Sphinx_VERSION + ERROR_VARIABLE _Sphinx_VERSION + ) + endif () + + # The sphinx version can also contain a "b" instead of the last dot. + # For example "Sphinx v1.2b1" or "Sphinx 1.7.0b2" so we cannot just split on "." + if (_Sphinx_VERSION MATCHES "sphinx-build ([0-9]+\\.[0-9]+(\\.|a?|b?)([0-9]*)(b?)([0-9]*))") + set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") + set (_SPHINX_VERSION_FOUND) + elseif (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+(\\.|b?)([0-9]*)(b?)([0-9]*))") + set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") + set (_SPHINX_VERSION_FOUND) + elseif (_Sphinx_VERSION MATCHES "Sphinx \\(sphinx-build\\) ([0-9]+\\.[0-9]+(\\.|a?|b?)([0-9]*)(b?)([0-9]*))") + set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") + set (_SPHINX_VERSION_FOUND) + endif () +endif () + +if(_SPHINX_VERSION_FOUND) + string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MAJOR ${Sphinx_VERSION_STRING}) + string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MINOR ${Sphinx_VERSION_STRING}) + string(REGEX REPLACE "[0-9]+\\.[0-9]+(\\.|b)([0-9]+)" "\\1" Sphinx_VERSION_PATCH ${Sphinx_VERSION_STRING}) + + # v1.2.0 -> v1.2 + if (Sphinx_VERSION_PATCH EQUAL 0) + string (REGEX REPLACE "\\.0$" "" Sphinx_VERSION_STRING "${Sphinx_VERSION_STRING}") + endif () +endif () + +# ---------------------------------------------------------------------------- +# compatibility with FindPythonInterp.cmake and FindPerl.cmake +set (SPHINX_EXECUTABLE "${Sphinx-build_EXECUTABLE}") + +# ---------------------------------------------------------------------------- +# handle the QUIETLY and REQUIRED arguments and set SPHINX_FOUND to TRUE if +# all listed variables are TRUE +include (FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS ( + Sphinx + REQUIRED_VARS + ${_Sphinx_REQUIRED_VARS} +# VERSION_VAR # This isn't available until CMake 2.8.8 so don't use it. + Sphinx_VERSION_STRING +) + +# ---------------------------------------------------------------------------- +# set Sphinx_DIR +if (NOT Sphinx_DIR AND Sphinx-build_EXECUTABLE) + get_filename_component (Sphinx_DIR "${Sphinx-build_EXECUTABLE}" PATH) + string (REGEX REPLACE "/bin/?" "" Sphinx_DIR "${Sphinx_DIR}") + set (Sphinx_DIR "${Sphinx_DIR}" CACHE PATH "Installation directory of Sphinx tools." FORCE) +endif () + +unset (_Sphinx_VERSION) +unset (_Sphinx_REQUIRED_VARS) \ No newline at end of file diff --git a/lib/jansson/cmake/janssonConfig.cmake.in b/lib/jansson/cmake/janssonConfig.cmake.in new file mode 100644 index 0000000..abd6793 --- /dev/null +++ b/lib/jansson/cmake/janssonConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/janssonTargets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/lib/jansson/cmake/jansson_config.h.cmake b/lib/jansson/cmake/jansson_config.h.cmake new file mode 100644 index 0000000..9fe1b53 --- /dev/null +++ b/lib/jansson/cmake/jansson_config.h.cmake @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The CMake system will generate the jansson_config.h file and + * copy it to the build and install directories. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* Define this so that we can disable scattered automake configuration in source files */ +#ifndef JANSSON_USING_CMAKE +#define JANSSON_USING_CMAKE +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#cmakedefine JSON_INTEGER_IS_LONG_LONG 1 + +/* Bring in the cmake-detected defines */ +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Include our standard type header for the integer typedef */ + +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_SYS_TYPES_H) +# include +#endif + + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @JSON_INLINE@ +#endif + + +#define json_int_t @JSON_INT_T@ +#define json_strtoint @JSON_STRTOINT@ +#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@ + + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS @JSON_HAVE_ATOMIC_BUILTINS@ + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS @JSON_HAVE_SYNC_BUILTINS@ + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/lib/jansson/cmake/jansson_private_config.h.cmake b/lib/jansson/cmake/jansson_private_config.h.cmake new file mode 100644 index 0000000..1fb14f4 --- /dev/null +++ b/lib/jansson/cmake/jansson_private_config.h.cmake @@ -0,0 +1,62 @@ +#cmakedefine HAVE_ENDIAN_H 1 +#cmakedefine HAVE_FCNTL_H 1 +#cmakedefine HAVE_SCHED_H 1 +#cmakedefine HAVE_UNISTD_H 1 +#cmakedefine HAVE_SYS_PARAM_H 1 +#cmakedefine HAVE_SYS_STAT_H 1 +#cmakedefine HAVE_SYS_TIME_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 +#cmakedefine HAVE_STDINT_H 1 + +#cmakedefine HAVE_CLOSE 1 +#cmakedefine HAVE_GETPID 1 +#cmakedefine HAVE_GETTIMEOFDAY 1 +#cmakedefine HAVE_OPEN 1 +#cmakedefine HAVE_READ 1 +#cmakedefine HAVE_SCHED_YIELD 1 + +#cmakedefine HAVE_SYNC_BUILTINS 1 +#cmakedefine HAVE_ATOMIC_BUILTINS 1 + +#cmakedefine HAVE_LOCALE_H 1 +#cmakedefine HAVE_SETLOCALE 1 + +#cmakedefine WORDS_BIGENDIAN 1 + +#cmakedefine HAVE_INT32_T 1 +#ifndef HAVE_INT32_T +# define int32_t @JSON_INT32@ +#endif + +#cmakedefine HAVE_UINT32_T 1 +#ifndef HAVE_UINT32_T +# define uint32_t @JSON_UINT32@ +#endif + +#cmakedefine HAVE_UINT16_T 1 +#ifndef HAVE_UINT16_T +# define uint16_t @JSON_UINT16@ +#endif + +#cmakedefine HAVE_UINT8_T 1 +#ifndef HAVE_UINT8_T +# define uint8_t @JSON_UINT8@ +#endif + +#cmakedefine HAVE_SSIZE_T 1 + +#ifndef HAVE_SSIZE_T +# define ssize_t @JSON_SSIZE@ +#endif + +#cmakedefine USE_URANDOM 1 +#cmakedefine USE_WINDOWS_CRYPTOAPI 1 + +#cmakedefine USE_DTOA 1 +#if USE_DTOA +# define DTOA_ENABLED 1 +#else +# define DTOA_ENABLED 0 +#endif + +#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@ diff --git a/lib/jansson/configure.ac b/lib/jansson/configure.ac new file mode 100644 index 0000000..195ff72 --- /dev/null +++ b/lib/jansson/configure.ac @@ -0,0 +1,181 @@ +AC_PREREQ([2.72]) +AC_INIT([jansson],[2.14],[https://github.com/akheron/jansson/issues]) + +AC_CONFIG_AUX_DIR([.]) +AM_INIT_AUTOMAKE([1.10 foreign]) + +AC_CONFIG_SRCDIR([src/value.c]) +AC_CONFIG_HEADERS([jansson_private_config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_PROG_LIBTOOL +AM_CONDITIONAL([GCC], [test x$GCC = xyes]) + +# Checks for libraries. + +# Checks for header files. +AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/stat.h sys/time.h sys/types.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_INT32_T +AC_TYPE_UINT32_T +AC_TYPE_UINT16_T +AC_TYPE_UINT8_T +AC_TYPE_LONG_LONG_INT + +AC_C_BIGENDIAN + +AC_C_INLINE +case $ac_cv_c_inline in + yes) json_inline=inline;; + no) json_inline=;; + *) json_inline=$ac_cv_c_inline;; +esac +AC_SUBST([json_inline]) + +# Checks for library functions. +AC_CHECK_FUNCS([close getpid gettimeofday open read setlocale sched_yield strtoll]) + +AC_MSG_CHECKING([for gcc __sync builtins]) +have_sync_builtins=no +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1);]])],[have_sync_builtins=yes],[]) +if test "x$have_sync_builtins" = "xyes"; then + AC_DEFINE([HAVE_SYNC_BUILTINS], [1], + [Define to 1 if gcc's __sync builtins are available]) + json_have_sync_builtins=1 +else + json_have_sync_builtins=0 +fi +AC_SUBST([json_have_sync_builtins]) +AC_MSG_RESULT([$have_sync_builtins]) + +AC_MSG_CHECKING([for gcc __atomic builtins]) +have_atomic_builtins=no +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE);]])],[have_atomic_builtins=yes],[]) +if test "x$have_atomic_builtins" = "xyes"; then + AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], + [Define to 1 if gcc's __atomic builtins are available]) + json_have_atomic_builtins=1 +else + json_have_atomic_builtins=0 +fi +AC_SUBST([json_have_atomic_builtins]) +AC_MSG_RESULT([$have_atomic_builtins]) + +case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in + yesyes) json_have_long_long=1;; + *) json_have_long_long=0;; +esac +AC_SUBST([json_have_long_long]) + +# Features +AC_ARG_ENABLE([urandom], + [AS_HELP_STRING([--disable-urandom], + [Don't use /dev/urandom to seed the hash function])], + [use_urandom=$enableval], [use_urandom=yes]) + +if test "x$use_urandom" = xyes; then +AC_DEFINE([USE_URANDOM], [1], + [Define to 1 if /dev/urandom should be used for seeding the hash function]) +fi + +AC_ARG_ENABLE([windows-cryptoapi], + [AS_HELP_STRING([--disable-windows-cryptoapi], + [Don't use CryptGenRandom to seed the hash function])], + [use_windows_cryptoapi=$enableval], [use_windows_cryptoapi=yes]) + +if test "x$use_windows_cryptoapi" = xyes; then +AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1], + [Define to 1 if CryptGenRandom should be used for seeding the hash function]) +fi + +AC_ARG_ENABLE([initial-hashtable-order], + [AS_HELP_STRING([--enable-initial-hashtable-order=VAL], + [Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.])], + [initial_hashtable_order=$enableval], [initial_hashtable_order=3]) +AC_DEFINE_UNQUOTED([INITIAL_HASHTABLE_ORDER], [$initial_hashtable_order], + [Number of buckets new object hashtables contain is 2 raised to this power. E.g. 3 -> 2^3 = 8.]) + +AC_ARG_ENABLE([Bsymbolic], + [AS_HELP_STRING([--disable-Bsymbolic], + [Avoid linking with -Bsymbolic-function])], + [], [with_Bsymbolic=check]) + +if test "x$with_Bsymbolic" != "xno" ; then + AC_MSG_CHECKING([for -Bsymbolic-functions linker flag]) + saved_LDFLAGS="${LDFLAGS}" + LDFLAGS=-Wl,-Bsymbolic-functions + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[int main (void) { return 0; }]])],[AC_MSG_RESULT([yes]) + have_Bsymbolic=yes],[AC_MSG_RESULT([no]) + have_Bsymbolic=no + ]) + LDFLAGS="${saved_LDFLAGS}" + + if test "x$with_Bsymbolic" = "xcheck" ; then + with_Bsymbolic=$have_Bsymbolic; + fi + if test "x$with_Bsymbolic:x$have_Bsymbolic" = "xyes:xno" ; then + AC_MSG_ERROR([linker support is required for -Bsymbolic]) + fi +fi + +AS_IF([test "x$with_Bsymbolic" = "xyes"], [JSON_BSYMBOLIC_LDFLAGS=-Wl[,]-Bsymbolic-functions]) +AC_SUBST(JSON_BSYMBOLIC_LDFLAGS) + +# Enable symbol versioning on GNU libc +JSON_SYMVER_LDFLAGS= +AC_CHECK_DECL([__GLIBC__], [JSON_SYMVER_LDFLAGS=-Wl,--default-symver]) +AC_SUBST([JSON_SYMVER_LDFLAGS]) + +AC_ARG_ENABLE([dtoa], + [AS_HELP_STRING([--enable-dtoa], [Use dtoa for optimal floating point to string conversion])], + [case "$enableval" in + yes) dtoa=yes ;; + no) dtoa=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-dtoa]) ;; + esac], [dtoa=yes]) +if test "$dtoa" = "yes"; then + AC_DEFINE([DTOA_ENABLED], [1], + [Define to 1 to use dtoa to convert floating points to strings]) +fi +AM_CONDITIONAL([DTOA_ENABLED], [test "$dtoa" = "yes"]) + +AC_ARG_ENABLE([ossfuzzers], + [AS_HELP_STRING([--enable-ossfuzzers], + [Whether to generate the fuzzers for OSS-Fuzz])], + [have_ossfuzzers=yes], [have_ossfuzzers=no]) +AM_CONDITIONAL([USE_OSSFUZZERS], [test "x$have_ossfuzzers" = "xyes"]) + + +AC_SUBST([LIB_FUZZING_ENGINE]) +AM_CONDITIONAL([USE_OSSFUZZ_FLAG], [test "x$LIB_FUZZING_ENGINE" = "x-fsanitize=fuzzer"]) +AM_CONDITIONAL([USE_OSSFUZZ_STATIC], [test -f "$LIB_FUZZING_ENGINE"]) + + +if test x$GCC = xyes; then + AC_MSG_CHECKING(for -Wno-format-truncation) + wnoformat_truncation="-Wno-format-truncation" + AS_IF([${CC} -Wno-format-truncation -Werror -S -o /dev/null -xc /dev/null > /dev/null 2>&1], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + wnoformat_truncation=""]) + + AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement -Wshadow ${wnoformat_truncation}" +fi +AC_SUBST([AM_CFLAGS]) + +AC_CONFIG_FILES([ + jansson.pc + Makefile + doc/Makefile + src/Makefile + src/jansson_config.h + test/Makefile + test/bin/Makefile + test/ossfuzz/Makefile + test/suites/Makefile + test/suites/api/Makefile +]) +AC_OUTPUT diff --git a/lib/jansson/doc/.gitignore b/lib/jansson/doc/.gitignore new file mode 100644 index 0000000..69fa449 --- /dev/null +++ b/lib/jansson/doc/.gitignore @@ -0,0 +1 @@ +_build/ diff --git a/lib/jansson/doc/Makefile.am b/lib/jansson/doc/Makefile.am new file mode 100644 index 0000000..8186a7d --- /dev/null +++ b/lib/jansson/doc/Makefile.am @@ -0,0 +1,20 @@ +EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \ + gettingstarted.rst github_commits.c index.rst threadsafety.rst \ + tutorial.rst upgrading.rst ext/refcounting.py + +SPHINXBUILD = sphinx-build +SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA) + +html-local: + $(SPHINXBUILD) -b html $(SPHINXOPTS) $(srcdir) _build/html + +install-html-local: html + mkdir -p $(DESTDIR)$(htmldir) + cp -r _build/html $(DESTDIR)$(htmldir) + +uninstall-local: + rm -rf $(DESTDIR)$(htmldir) + +clean-local: + rm -rf _build + rm -f ext/refcounting.pyc diff --git a/lib/jansson/doc/README b/lib/jansson/doc/README new file mode 100644 index 0000000..930b3bf --- /dev/null +++ b/lib/jansson/doc/README @@ -0,0 +1,5 @@ +To build the documentation, invoke + + make html + +Then point your browser to _build/html/index.html. diff --git a/lib/jansson/doc/apiref.rst b/lib/jansson/doc/apiref.rst new file mode 100644 index 0000000..4bfb687 --- /dev/null +++ b/lib/jansson/doc/apiref.rst @@ -0,0 +1,2064 @@ +.. _apiref: + +************* +API Reference +************* + +.. highlight:: c + +Preliminaries +============= + +All declarations are in :file:`jansson.h`, so it's enough to + +:: + + #include + +in each source file. + +All constants are prefixed with ``JSON_`` (except for those describing +the library version, prefixed with ``JANSSON_``). Other identifiers +are prefixed with ``json_``. Type names are suffixed with ``_t`` and +``typedef``\ 'd so that the ``struct`` keyword need not be used. + + +Library Version +=============== + +The Jansson version is of the form *A.B.C*, where *A* is the major +version, *B* is the minor version and *C* is the micro version. If the +micro version is zero, it's omitted from the version string, i.e. the +version string is just *A.B*. + +When a new release only fixes bugs and doesn't add new features or +functionality, the micro version is incremented. When new features are +added in a backwards compatible way, the minor version is incremented +and the micro version is set to zero. When there are backwards +incompatible changes, the major version is incremented and others are +set to zero. + +The following preprocessor constants specify the current version of +the library: + +``JANSSON_MAJOR_VERSION``, ``JANSSON_MINOR_VERSION``, ``JANSSON_MICRO_VERSION`` + Integers specifying the major, minor and micro versions, + respectively. + +``JANSSON_VERSION`` + A string representation of the current version, e.g. ``"1.2.1"`` or + ``"1.3"``. + +``JANSSON_VERSION_HEX`` + A 3-byte hexadecimal representation of the version, e.g. + ``0x010201`` for version 1.2.1 and ``0x010300`` for version 1.3. + This is useful in numeric comparisons, e.g.:: + + #if JANSSON_VERSION_HEX >= 0x010300 + /* Code specific to version 1.3 and above */ + #endif + +Additionally, there are functions to determine the version of Jansson at +runtime: + +.. function:: const char *jansson_version_str() + + Return the version of the Jansson library, in the same format as + the ``JANSSON_VERSION`` preprocessor constant. + + .. versionadded:: 2.13 + +.. function:: int jansson_version_cmp(int major, int minor, int micro) + + Returns an integer less than, equal to, or greater than zero if + the runtime version of Jansson is found, respectively, to be less + than, to match, or be greater than the provided *major*, *minor*, and + *micro*. + + .. versionadded:: 2.13 + +``JANSSON_THREAD_SAFE_REFCOUNT`` + If this value is defined all read-only operations and reference counting in + Jansson are thread safe. This value is not defined for versions older than + ``2.11`` or when the compiler does not provide built-in atomic functions. + + +Value Representation +==================== + +The JSON specification (:rfc:`4627`) defines the following data types: +*object*, *array*, *string*, *number*, *boolean*, and *null*. JSON +types are used dynamically; arrays and objects can hold any other data +type, including themselves. For this reason, Jansson's type system is +also dynamic in nature. There's one C type to represent all JSON +values, and this structure knows the type of the JSON value it holds. + +.. type:: json_t + + This data structure is used throughout the library to represent all + JSON values. It always contains the type of the JSON value it holds + and the value's reference count. The rest depends on the type of the + value. + +Objects of :type:`json_t` are always used through a pointer. There +are APIs for querying the type, manipulating the reference count, and +for constructing and manipulating values of different types. + +Unless noted otherwise, all API functions return an error value if an +error occurs. Depending on the function's signature, the error value +is either *NULL* or -1. Invalid arguments or invalid input are +apparent sources for errors. Memory allocation and I/O operations may +also cause errors. + + +Type +---- + +.. c:enum:: json_type + + The type of a JSON value. The following members are defined: + + +--------------------+ + | ``JSON_OBJECT`` | + +--------------------+ + | ``JSON_ARRAY`` | + +--------------------+ + | ``JSON_STRING`` | + +--------------------+ + | ``JSON_INTEGER`` | + +--------------------+ + | ``JSON_REAL`` | + +--------------------+ + | ``JSON_TRUE`` | + +--------------------+ + | ``JSON_FALSE`` | + +--------------------+ + | ``JSON_NULL`` | + +--------------------+ + + These correspond to JSON object, array, string, number, boolean and + null. A number is represented by either a value of the type + ``JSON_INTEGER`` or of the type ``JSON_REAL``. A true boolean value + is represented by a value of the type ``JSON_TRUE`` and false by a + value of the type ``JSON_FALSE``. + +.. function:: int json_typeof(const json_t *json) + + Return the type of the JSON value (a :type:`json_type` cast to + ``int``). *json* MUST NOT be *NULL*. This function is actually + implemented as a macro for speed. + +.. function:: int json_is_object(const json_t *json) + int json_is_array(const json_t *json) + int json_is_string(const json_t *json) + int json_is_integer(const json_t *json) + int json_is_real(const json_t *json) + int json_is_true(const json_t *json) + int json_is_false(const json_t *json) + int json_is_null(const json_t *json) + + These functions (actually macros) return true (non-zero) for values + of the given type, and false (zero) for values of other types and + for *NULL*. + +.. function:: int json_is_number(const json_t *json) + + Returns true for values of types ``JSON_INTEGER`` and + ``JSON_REAL``, and false for other types and for *NULL*. + +.. function:: int json_is_boolean(const json_t *json) + + Returns true for types ``JSON_TRUE`` and ``JSON_FALSE``, and false + for values of other types and for *NULL*. + +.. function:: int json_boolean_value(const json_t *json) + + Alias of :func:`json_is_true()`, i.e. returns 1 for ``JSON_TRUE`` + and 0 otherwise. + + .. versionadded:: 2.7 + + +.. _apiref-reference-count: + +Reference Count +--------------- + +The reference count is used to track whether a value is still in use +or not. When a value is created, it's reference count is set to 1. If +a reference to a value is kept (e.g. a value is stored somewhere for +later use), its reference count is incremented, and when the value is +no longer needed, the reference count is decremented. When the +reference count drops to zero, there are no references left, and the +value can be destroyed. + +.. function:: json_t *json_incref(json_t *json) + + Increment the reference count of *json* if it's not *NULL*. + Returns *json*. + +.. function:: void json_decref(json_t *json) + + Decrement the reference count of *json*. As soon as a call to + :func:`json_decref()` drops the reference count to zero, the value + is destroyed and it can no longer be used. + +Functions creating new JSON values set the reference count to 1. These +functions are said to return a **new reference**. Other functions +returning (existing) JSON values do not normally increase the +reference count. These functions are said to return a **borrowed +reference**. So, if the user will hold a reference to a value returned +as a borrowed reference, he must call :func:`json_incref`. As soon as +the value is no longer needed, :func:`json_decref` should be called +to release the reference. + +Normally, all functions accepting a JSON value as an argument will +manage the reference, i.e. increase and decrease the reference count +as needed. However, some functions **steal** the reference, i.e. they +have the same result as if the user called :func:`json_decref()` on +the argument right after calling the function. These functions are +suffixed with ``_new`` or have ``_new_`` somewhere in their name. + +For example, the following code creates a new JSON array and appends +an integer to it:: + + json_t *array, *integer; + + array = json_array(); + integer = json_integer(42); + + json_array_append(array, integer); + json_decref(integer); + +Note how the caller has to release the reference to the integer value +by calling :func:`json_decref()`. By using a reference stealing +function :func:`json_array_append_new()` instead of +:func:`json_array_append()`, the code becomes much simpler:: + + json_t *array = json_array(); + json_array_append_new(array, json_integer(42)); + +In this case, the user doesn't have to explicitly release the +reference to the integer value, as :func:`json_array_append_new()` +steals the reference when appending the value to the array. + +In the following sections it is clearly documented whether a function +will return a new or borrowed reference or steal a reference to its +argument. + + +Circular References +------------------- + +A circular reference is created when an object or an array is, +directly or indirectly, inserted inside itself. The direct case is +simple:: + + json_t *obj = json_object(); + json_object_set(obj, "foo", obj); + +Jansson will refuse to do this, and :func:`json_object_set()` (and +all the other such functions for objects and arrays) will return with +an error status. The indirect case is the dangerous one:: + + json_t *arr1 = json_array(), *arr2 = json_array(); + json_array_append(arr1, arr2); + json_array_append(arr2, arr1); + +In this example, the array ``arr2`` is contained in the array +``arr1``, and vice versa. Jansson cannot check for this kind of +indirect circular references without a performance hit, so it's up to +the user to avoid them. + +If a circular reference is created, the memory consumed by the values +cannot be freed by :func:`json_decref()`. The reference counts never +drops to zero because the values are keeping the references to each +other. Moreover, trying to encode the values with any of the encoding +functions will fail. The encoder detects circular references and +returns an error status. + +Scope Dereferencing +------------------- + +.. versionadded:: 2.9 + +It is possible to use the ``json_auto_t`` type to automatically +dereference a value at the end of a scope. For example:: + + void function(void) { + json_auto_t *value = NULL; + value = json_string("foo"); + /* json_decref(value) is automatically called. */ + } + +This feature is only available on GCC and Clang. So if your project +has a portability requirement for other compilers, you should avoid +this feature. + +Additionally, as always, care should be taken when passing values to +functions that steal references. + +True, False and Null +==================== + +These three values are implemented as singletons, so the returned +pointers won't change between invocations of these functions. + +.. function:: json_t *json_true(void) + + .. refcounting:: new + + Returns the JSON true value. + +.. function:: json_t *json_false(void) + + .. refcounting:: new + + Returns the JSON false value. + +.. function:: json_t *json_boolean(val) + + .. refcounting:: new + + Returns JSON false if ``val`` is zero, and JSON true otherwise. + This is a macro, and equivalent to ``val ? json_true() : + json_false()``. + + .. versionadded:: 2.4 + + +.. function:: json_t *json_null(void) + + .. refcounting:: new + + Returns the JSON null value. + + +String +====== + +Jansson uses UTF-8 as the character encoding. All JSON strings must be +valid UTF-8 (or ASCII, as it's a subset of UTF-8). All Unicode +codepoints U+0000 through U+10FFFF are allowed, but you must use +length-aware functions if you wish to embed null bytes in strings. + +.. function:: json_t *json_string(const char *value) + + .. refcounting:: new + + Returns a new JSON string, or *NULL* on error. *value* must be a + valid null terminated UTF-8 encoded Unicode string. + +.. function:: json_t *json_stringn(const char *value, size_t len) + + .. refcounting:: new + + Like :func:`json_string`, but with explicit length, so *value* may + contain null characters or not be null terminated. + + .. versionadded:: 2.7 + +.. function:: json_t *json_string_nocheck(const char *value) + + .. refcounting:: new + + Like :func:`json_string`, but doesn't check that *value* is valid + UTF-8. Use this function only if you are certain that this really + is the case (e.g. you have already checked it by other means). + +.. function:: json_t *json_stringn_nocheck(const char *value, size_t len) + + .. refcounting:: new + + Like :func:`json_string_nocheck`, but with explicit length, so + *value* may contain null characters or not be null terminated. + + .. versionadded:: 2.7 + +.. function:: const char *json_string_value(const json_t *string) + + Returns the associated value of *string* as a null terminated UTF-8 + encoded string, or *NULL* if *string* is not a JSON string. + + The returned value is read-only and must not be modified or freed by + the user. It is valid as long as *string* exists, i.e. as long as + its reference count has not dropped to zero. + +.. function:: size_t json_string_length(const json_t *string) + + Returns the length of *string* in its UTF-8 presentation, or zero + if *string* is not a JSON string. + + .. versionadded:: 2.7 + +.. function:: int json_string_set(json_t *string, const char *value) + + Sets the associated value of *string* to *value*. *value* must be a + valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on + error. + +.. function:: int json_string_setn(json_t *string, const char *value, size_t len) + + Like :func:`json_string_set`, but with explicit length, so *value* + may contain null characters or not be null terminated. + + .. versionadded:: 2.7 + +.. function:: int json_string_set_nocheck(json_t *string, const char *value) + + Like :func:`json_string_set`, but doesn't check that *value* is + valid UTF-8. Use this function only if you are certain that this + really is the case (e.g. you have already checked it by other + means). + +.. function:: int json_string_setn_nocheck(json_t *string, const char *value, size_t len) + + Like :func:`json_string_set_nocheck`, but with explicit length, + so *value* may contain null characters or not be null terminated. + + .. versionadded:: 2.7 + +.. function:: json_t *json_sprintf(const char *format, ...) + json_t *json_vsprintf(const char *format, va_list ap) + + .. refcounting:: new + + Construct a JSON string from a format string and varargs, just like + :func:`printf()`. + + .. versionadded:: 2.11 + + +Number +====== + +The JSON specification only contains one numeric type, "number". The C +programming language has distinct types for integer and floating-point +numbers, so for practical reasons Jansson also has distinct types for +the two. They are called "integer" and "real", respectively. For more +information, see :ref:`rfc-conformance`. + +.. type:: json_int_t + + This is the C type that is used to store JSON integer values. It + represents the widest integer type available on your system. In + practice it's just a typedef of ``long long`` if your compiler + supports it, otherwise ``long``. + + Usually, you can safely use plain ``int`` in place of + ``json_int_t``, and the implicit C integer conversion handles the + rest. Only when you know that you need the full 64-bit range, you + should use ``json_int_t`` explicitly. + +``JSON_INTEGER_IS_LONG_LONG`` + This is a preprocessor variable that holds the value 1 if + :type:`json_int_t` is ``long long``, and 0 if it's ``long``. It + can be used as follows:: + + #if JSON_INTEGER_IS_LONG_LONG + /* Code specific for long long */ + #else + /* Code specific for long */ + #endif + +``JSON_INTEGER_FORMAT`` + This is a macro that expands to a :func:`printf()` conversion + specifier that corresponds to :type:`json_int_t`, without the + leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro + is required because the actual type of :type:`json_int_t` can be + either ``long`` or ``long long``, and :func:`printf()` requires + different length modifiers for the two. + + Example:: + + json_int_t x = 123123123; + printf("x is %" JSON_INTEGER_FORMAT "\n", x); + + +.. function:: json_t *json_integer(json_int_t value) + + .. refcounting:: new + + Returns a new JSON integer, or *NULL* on error. + +.. function:: json_int_t json_integer_value(const json_t *integer) + + Returns the associated value of *integer*, or 0 if *json* is not a + JSON integer. + +.. function:: int json_integer_set(const json_t *integer, json_int_t value) + + Sets the associated value of *integer* to *value*. Returns 0 on + success and -1 if *integer* is not a JSON integer. + +.. function:: json_t *json_real(double value) + + .. refcounting:: new + + Returns a new JSON real, or *NULL* on error. + +.. function:: double json_real_value(const json_t *real) + + Returns the associated value of *real*, or 0.0 if *real* is not a + JSON real. + +.. function:: int json_real_set(const json_t *real, double value) + + Sets the associated value of *real* to *value*. Returns 0 on + success and -1 if *real* is not a JSON real. + +.. function:: double json_number_value(const json_t *json) + + Returns the associated value of the JSON integer or JSON real + *json*, cast to double regardless of the actual type. If *json* is + neither JSON real nor JSON integer, 0.0 is returned. + + +Array +===== + +A JSON array is an ordered collection of other JSON values. + +.. function:: json_t *json_array(void) + + .. refcounting:: new + + Returns a new JSON array, or *NULL* on error. Initially, the array + is empty. + +.. function:: size_t json_array_size(const json_t *array) + + Returns the number of elements in *array*, or 0 if *array* is NULL + or not a JSON array. + +.. function:: json_t *json_array_get(const json_t *array, size_t index) + + .. refcounting:: borrow + + Returns the element in *array* at position *index*. The valid range + for *index* is from 0 to the return value of + :func:`json_array_size()` minus 1. If *array* is not a JSON array, + if *array* is *NULL*, or if *index* is out of range, *NULL* is + returned. + +.. function:: int json_array_set(json_t *array, size_t index, json_t *value) + + Replaces the element in *array* at position *index* with *value*. + The valid range for *index* is from 0 to the return value of + :func:`json_array_size()` minus 1. Returns 0 on success and -1 on + error. + +.. function:: int json_array_set_new(json_t *array, size_t index, json_t *value) + + Like :func:`json_array_set()` but steals the reference to *value*. + This is useful when *value* is newly created and not used after + the call. + +.. function:: int json_array_append(json_t *array, json_t *value) + + Appends *value* to the end of *array*, growing the size of *array* + by 1. Returns 0 on success and -1 on error. + +.. function:: int json_array_append_new(json_t *array, json_t *value) + + Like :func:`json_array_append()` but steals the reference to + *value*. This is useful when *value* is newly created and not used + after the call. + +.. function:: int json_array_insert(json_t *array, size_t index, json_t *value) + + Inserts *value* to *array* at position *index*, shifting the + elements at *index* and after it one position towards the end of + the array. Returns 0 on success and -1 on error. + +.. function:: int json_array_insert_new(json_t *array, size_t index, json_t *value) + + Like :func:`json_array_insert()` but steals the reference to + *value*. This is useful when *value* is newly created and not used + after the call. + +.. function:: int json_array_remove(json_t *array, size_t index) + + Removes the element in *array* at position *index*, shifting the + elements after *index* one position towards the start of the array. + Returns 0 on success and -1 on error. The reference count of the + removed value is decremented. + +.. function:: int json_array_clear(json_t *array) + + Removes all elements from *array*. Returns 0 on success and -1 on + error. The reference count of all removed values are decremented. + +.. function:: int json_array_extend(json_t *array, json_t *other_array) + + Appends all elements in *other_array* to the end of *array*. + Returns 0 on success and -1 on error. + +.. function:: void json_array_foreach(array, index, value) + + Iterate over every element of ``array``, running the block + of code that follows each time with the proper values set to + variables ``index`` and ``value``, of types :type:`size_t` and + :type:`json_t` pointer respectively. Example:: + + /* array is a JSON array */ + size_t index; + json_t *value; + + json_array_foreach(array, index, value) { + /* block of code that uses index and value */ + } + + The items are returned in increasing index order. + + This macro expands to an ordinary ``for`` statement upon + preprocessing, so its performance is equivalent to that of + hand-written code using the array access functions. + The main advantage of this macro is that it abstracts + away the complexity, and makes for more concise and readable code. + + .. versionadded:: 2.5 + + +Object +====== + +A JSON object is a dictionary of key-value pairs, where the key is a +Unicode string and the value is any JSON value. + +Even though null bytes are allowed in string values, they are not +allowed in object keys. + +.. function:: json_t *json_object(void) + + .. refcounting:: new + + Returns a new JSON object, or *NULL* on error. Initially, the + object is empty. + +.. function:: size_t json_object_size(const json_t *object) + + Returns the number of elements in *object*, or 0 if *object* is not + a JSON object. + +.. function:: json_t *json_object_get(const json_t *object, const char *key) + + .. refcounting:: borrow + + Get a value corresponding to *key* from *object*. Returns *NULL* if + *key* is not found and on error. + +.. function:: json_t *json_object_getn(const json_t *object, const char *key, size_t key_len) + + .. refcounting:: borrow + + Like :func:`json_object_get`, but give the fixed-length *key* with length *key_len*. + See :ref:`fixed_length_keys` for details. + + .. versionadded:: 2.14 + +.. function:: int json_object_set(json_t *object, const char *key, json_t *value) + + Set the value of *key* to *value* in *object*. *key* must be a + valid null terminated UTF-8 encoded Unicode string. If there + already is a value for *key*, it is replaced by the new value. + Returns 0 on success and -1 on error. + +.. function:: int json_object_setn(json_t *object, const char *key, size_t key_len, json_t *value) + + Like :func:`json_object_set`, but give the fixed-length *key* with length *key_len*. + See :ref:`fixed_length_keys` for details. + + .. versionadded:: 2.14 + +.. function:: int json_object_set_nocheck(json_t *object, const char *key, json_t *value) + + Like :func:`json_object_set`, but doesn't check that *key* is + valid UTF-8. Use this function only if you are certain that this + really is the case (e.g. you have already checked it by other + means). + +.. function:: int json_object_setn_nocheck(json_t *object, const char *key, size_t key_len, json_t *value) + + Like :func:`json_object_set_nocheck`, but give the fixed-length *key* with length *key_len*. + See :ref:`fixed_length_keys` for details. + + .. versionadded:: 2.14 + +.. function:: int json_object_set_new(json_t *object, const char *key, json_t *value) + + Like :func:`json_object_set()` but steals the reference to + *value*. This is useful when *value* is newly created and not used + after the call. + +.. function:: int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value) + + Like :func:`json_object_set_new`, but give the fixed-length *key* with length *key_len*. + See :ref:`fixed_length_keys` for details. + + .. versionadded:: 2.14 + +.. function:: int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value) + + Like :func:`json_object_set_new`, but doesn't check that *key* is + valid UTF-8. Use this function only if you are certain that this + really is the case (e.g. you have already checked it by other + means). + +.. function:: int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len, json_t *value) + + Like :func:`json_object_set_new_nocheck`, but give the fixed-length *key* with length *key_len*. + See :ref:`fixed_length_keys` for details. + + .. versionadded:: 2.14 + +.. function:: int json_object_del(json_t *object, const char *key) + + Delete *key* from *object* if it exists. Returns 0 on success, or + -1 if *key* was not found. The reference count of the removed value + is decremented. + +.. function:: int json_object_deln(json_t *object, const char *key, size_t key_len) + + Like :func:`json_object_del`, but give the fixed-length *key* with length *key_len*. + See :ref:`fixed_length_keys` for details. + + .. versionadded:: 2.14 + +.. function:: int json_object_clear(json_t *object) + + Remove all elements from *object*. Returns 0 on success and -1 if + *object* is not a JSON object. The reference count of all removed + values are decremented. + +.. function:: int json_object_update(json_t *object, json_t *other) + + Update *object* with the key-value pairs from *other*, overwriting + existing keys. Returns 0 on success or -1 on error. + +.. function:: int json_object_update_existing(json_t *object, json_t *other) + + Like :func:`json_object_update()`, but only the values of existing + keys are updated. No new keys are created. Returns 0 on success or + -1 on error. + + .. versionadded:: 2.3 + +.. function:: int json_object_update_missing(json_t *object, json_t *other) + + Like :func:`json_object_update()`, but only new keys are created. + The value of any existing key is not changed. Returns 0 on success + or -1 on error. + + .. versionadded:: 2.3 + +.. function:: int json_object_update_new(json_t *object, json_t *other) + + Like :func:`json_object_update()`, but steals the reference to + *other*. This is useful when *other* is newly created and not used + after the call. + +.. function:: int json_object_update_existing_new(json_t *object, json_t *other) + + Like :func:`json_object_update_new()`, but only the values of existing + keys are updated. No new keys are created. Returns 0 on success or + -1 on error. + +.. function:: int json_object_update_missing_new(json_t *object, json_t *other) + + Like :func:`json_object_update_new()`, but only new keys are created. + The value of any existing key is not changed. Returns 0 on success + or -1 on error. + +.. function:: int json_object_update_recursive(json_t *object, json_t *other) + + Like :func:`json_object_update()`, but object values in *other* are + recursively merged with the corresponding values in *object* if they are also + objects, instead of overwriting them. Returns 0 on success or -1 on error. + +.. function:: void json_object_foreach(object, key, value) + + Iterate over every key-value pair of ``object``, running the block + of code that follows each time with the proper values set to + variables ``key`` and ``value``, of types ``const char *`` and + :type:`json_t` pointer respectively. Example:: + + /* obj is a JSON object */ + const char *key; + json_t *value; + + json_object_foreach(obj, key, value) { + /* block of code that uses key and value */ + } + + The items are returned in the order they were inserted to the + object. + + **Note:** It's not safe to call ``json_object_del(object, key)`` or ``json_object_deln(object, key, key_len)`` + during iteration. If you need to, use + :func:`json_object_foreach_safe` instead. + + This macro expands to an ordinary ``for`` statement upon + preprocessing, so its performance is equivalent to that of + hand-written iteration code using the object iteration protocol + (see below). The main advantage of this macro is that it abstracts + away the complexity behind iteration, and makes for more concise and + readable code. + + .. versionadded:: 2.3 + + +.. function:: void json_object_foreach_safe(object, tmp, key, value) + + Like :func:`json_object_foreach()`, but it's safe to call + ``json_object_del(object, key)`` or ``json_object_deln(object, key, key_len)`` during iteration. + You need to pass an extra ``void *`` parameter ``tmp`` that is used for temporary storage. + + .. versionadded:: 2.8 + +.. function:: void json_object_keylen_foreach(object, key, key_len, value) + + Like :c:func:`json_object_foreach`, but in *key_len* stored length of the *key*. + Example:: + + /* obj is a JSON object */ + const char *key; + json_t *value; + size_t len; + + json_object_keylen_foreach(obj, key, len, value) { + printf("got key %s with length %zu\n", key, len); + } + + **Note:** It's not safe to call ``json_object_deln(object, key, key_len)`` + during iteration. If you need to, use + :func:`json_object_keylen_foreach_safe` instead. + + .. versionadded:: 2.14 + + +.. function:: void json_object_keylen_foreach_safe(object, tmp, key, key_len, value) + + Like :func:`json_object_keylen_foreach()`, but it's safe to call + ``json_object_deln(object, key, key_len)`` during iteration. + You need to pass an extra ``void *`` parameter ``tmp`` that is used for temporary storage. + + .. versionadded:: 2.14 + +The following functions can be used to iterate through all key-value +pairs in an object. The items are returned in the order they were +inserted to the object. + +.. function:: void *json_object_iter(json_t *object) + + Returns an opaque iterator which can be used to iterate over all + key-value pairs in *object*, or *NULL* if *object* is empty. + +.. function:: void *json_object_iter_at(json_t *object, const char *key) + + Like :func:`json_object_iter()`, but returns an iterator to the + key-value pair in *object* whose key is equal to *key*, or NULL if + *key* is not found in *object*. Iterating forward to the end of + *object* only yields all key-value pairs of the object if *key* + happens to be the first key in the underlying hash table. + +.. function:: void *json_object_iter_next(json_t *object, void *iter) + + Returns an iterator pointing to the next key-value pair in *object* + after *iter*, or *NULL* if the whole object has been iterated + through. + +.. function:: const char *json_object_iter_key(void *iter) + + Extract the associated key from *iter*. + +.. function:: size_t json_object_iter_key_len(void *iter) + + Extract the associated key length from *iter*. + + .. versionadded:: 2.14 + +.. function:: json_t *json_object_iter_value(void *iter) + + .. refcounting:: borrow + + Extract the associated value from *iter*. + +.. function:: int json_object_iter_set(json_t *object, void *iter, json_t *value) + + Set the value of the key-value pair in *object*, that is pointed to + by *iter*, to *value*. + +.. function:: int json_object_iter_set_new(json_t *object, void *iter, json_t *value) + + Like :func:`json_object_iter_set()`, but steals the reference to + *value*. This is useful when *value* is newly created and not used + after the call. + +.. function:: void *json_object_key_to_iter(const char *key) + + Like :func:`json_object_iter_at()`, but much faster. Only works for + values returned by :func:`json_object_iter_key()`. Using other keys + will lead to segfaults. This function is used internally to + implement :func:`json_object_foreach`. Example:: + + /* obj is a JSON object */ + const char *key; + json_t *value; + + void *iter = json_object_iter(obj); + while(iter) + { + key = json_object_iter_key(iter); + value = json_object_iter_value(iter); + /* use key and value ... */ + iter = json_object_iter_next(obj, iter); + } + + .. versionadded:: 2.3 + +.. function:: void json_object_seed(size_t seed) + + Seed the hash function used in Jansson's hashtable implementation. + The seed is used to randomize the hash function so that an + attacker cannot control its output. + + If *seed* is 0, Jansson generates the seed itself by reading + random data from the operating system's entropy sources. If no + entropy sources are available, falls back to using a combination + of the current timestamp (with microsecond precision if possible) + and the process ID. + + If called at all, this function must be called before any calls to + :func:`json_object()`, either explicit or implicit. If this + function is not called by the user, the first call to + :func:`json_object()` (either explicit or implicit) seeds the hash + function. See :ref:`thread-safety` for notes on thread safety. + + If repeatable results are required, for e.g. unit tests, the hash + function can be "unrandomized" by calling :func:`json_object_seed` + with a constant value on program startup, e.g. + ``json_object_seed(1)``. + + .. versionadded:: 2.6 + + +Error reporting +=============== + +Jansson uses a single struct type to pass error information to the +user. See sections :ref:`apiref-decoding`, :ref:`apiref-pack` and +:ref:`apiref-unpack` for functions that pass error information using +this struct. + +.. type:: json_error_t + + .. member:: char text[] + + The error message (in UTF-8), or an empty string if a message is + not available. + + The last byte of this array contains a numeric error code. Use + :func:`json_error_code()` to extract this code. + + .. member:: char source[] + + Source of the error. This can be (a part of) the file name or a + special identifier in angle brackets (e.g. ````). + + .. member:: int line + + The line number on which the error occurred. + + .. member:: int column + + The column on which the error occurred. Note that this is the + *character column*, not the byte column, i.e. a multibyte UTF-8 + character counts as one column. + + .. member:: int position + + The position in bytes from the start of the input. This is + useful for debugging Unicode encoding problems. + +The normal use of :type:`json_error_t` is to allocate it on the stack, +and pass a pointer to a function. Example:: + + int main() { + json_t *json; + json_error_t error; + + json = json_load_file("/path/to/file.json", 0, &error); + if(!json) { + /* the error variable contains error information */ + } + ... + } + +Also note that if the call succeeded (``json != NULL`` in the above +example), the contents of ``error`` are generally left unspecified. +The decoding functions write to the ``position`` member also on +success. See :ref:`apiref-decoding` for more info. + +All functions also accept *NULL* as the :type:`json_error_t` pointer, +in which case no error information is returned to the caller. + +.. c:enum:: json_error_code + + An enumeration containing numeric error codes. The following errors are + currently defined: + + ``json_error_unknown`` + + Unknown error. This should only be returned for non-errorneous + :type:`json_error_t` structures. + + ``json_error_out_of_memory`` + + The library couldn’t allocate any heap memory. + + ``json_error_stack_overflow`` + + Nesting too deep. + + ``json_error_cannot_open_file`` + + Couldn’t open input file. + + ``json_error_invalid_argument`` + + A function argument was invalid. + + ``json_error_invalid_utf8`` + + The input string isn’t valid UTF-8. + + ``json_error_premature_end_of_input`` + + The input ended in the middle of a JSON value. + + ``json_error_end_of_input_expected`` + + There was some text after the end of a JSON value. See the + ``JSON_DISABLE_EOF_CHECK`` flag. + + ``json_error_invalid_syntax`` + + JSON syntax error. + + ``json_error_invalid_format`` + + Invalid format string for packing or unpacking. + + ``json_error_wrong_type`` + + When packing or unpacking, the actual type of a value differed from the + one specified in the format string. + + ``json_error_null_character`` + + A null character was detected in a JSON string. See the + ``JSON_ALLOW_NUL`` flag. + + ``json_error_null_value`` + + When packing or unpacking, some key or value was ``NULL``. + + ``json_error_null_byte_in_key`` + + An object key would contain a null byte. Jansson can’t represent such + keys; see :ref:`rfc-conformance`. + + ``json_error_duplicate_key`` + + Duplicate key in object. See the ``JSON_REJECT_DUPLICATES`` flag. + + ``json_error_numeric_overflow`` + + When converting a JSON number to a C numeric type, a numeric overflow + was detected. + + ``json_error_item_not_found`` + + Key in object not found. + + ``json_error_index_out_of_range`` + + Array index is out of range. + + .. versionadded:: 2.11 + +.. function:: enum json_error_code json_error_code(const json_error_t *error) + + Returns the error code embedded in ``error->text``. + + .. versionadded:: 2.11 + + +Encoding +======== + +This section describes the functions that can be used to encode +values to JSON. By default, only objects and arrays can be encoded +directly, since they are the only valid *root* values of a JSON text. +To encode any JSON value, use the ``JSON_ENCODE_ANY`` flag (see +below). + +By default, the output has no newlines, and spaces are used between +array and object elements for a readable output. This behavior can be +altered by using the ``JSON_INDENT`` and ``JSON_COMPACT`` flags +described below. A newline is never appended to the end of the encoded +JSON data. + +Each function takes a *flags* parameter that controls some aspects of +how the data is encoded. Its default value is 0. The following macros +can be ORed together to obtain *flags*. + +``JSON_INDENT(n)`` + Pretty-print the result, using newlines between array and object + items, and indenting with *n* spaces. The valid range for *n* is + between 0 and 31 (inclusive), other values result in an undefined + output. If ``JSON_INDENT`` is not used or *n* is 0, no newlines are + inserted between array and object items. + + The ``JSON_MAX_INDENT`` constant defines the maximum indentation + that can be used, and its value is 31. + + .. versionchanged:: 2.7 + Added ``JSON_MAX_INDENT``. + +``JSON_COMPACT`` + This flag enables a compact representation, i.e. sets the separator + between array and object items to ``","`` and between object keys + and values to ``":"``. Without this flag, the corresponding + separators are ``", "`` and ``": "`` for more readable output. + +``JSON_ENSURE_ASCII`` + If this flag is used, the output is guaranteed to consist only of + ASCII characters. This is achieved by escaping all Unicode + characters outside the ASCII range. + +``JSON_SORT_KEYS`` + If this flag is used, all the objects in output are sorted by key. + This is useful e.g. if two JSON texts are diffed or visually + compared. + +``JSON_PRESERVE_ORDER`` + **Deprecated since version 2.8:** Order of object keys + is always preserved. + + Prior to version 2.8: If this flag is used, object keys in the + output are sorted into the same order in which they were first + inserted to the object. For example, decoding a JSON text and then + encoding with this flag preserves the order of object keys. + +``JSON_ENCODE_ANY`` + Specifying this flag makes it possible to encode any JSON value on + its own. Without it, only objects and arrays can be passed as the + *json* value to the encoding functions. + + **Note:** Encoding any value may be useful in some scenarios, but + it's generally discouraged as it violates strict compatibility with + :rfc:`4627`. If you use this flag, don't expect interoperability + with other JSON systems. + + .. versionadded:: 2.1 + +``JSON_ESCAPE_SLASH`` + Escape the ``/`` characters in strings with ``\/``. + + .. versionadded:: 2.4 + +``JSON_REAL_PRECISION(n)`` + Output all real numbers with at most *n* digits of precision. The + valid range for *n* is between 0 and 31 (inclusive), and other + values result in an undefined behavior. + + By default, the precision is 17, to correctly and losslessly encode + all IEEE 754 double precision floating point numbers. + + .. versionadded:: 2.7 + +``JSON_EMBED`` + If this flag is used, the opening and closing characters of the top-level + array ('[', ']') or object ('{', '}') are omitted during encoding. This + flag is useful when concatenating multiple arrays or objects into a stream. + + .. versionadded:: 2.10 + +These functions output UTF-8: + +.. function:: char *json_dumps(const json_t *json, size_t flags) + + Returns the JSON representation of *json* as a string, or *NULL* on + error. *flags* is described above. The return value must be freed + by the caller using :func:`free()`. Note that if you have called + :func:`json_set_alloc_funcs()` to override :func:`free()`, you should + call your custom free function instead to free the return value. + +.. function:: size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) + + Writes the JSON representation of *json* to the *buffer* of + *size* bytes. Returns the number of bytes that would be written + or 0 on error. *flags* is described above. *buffer* is not + null-terminated. + + This function never writes more than *size* bytes. If the return + value is greater than *size*, the contents of the *buffer* are + undefined. This behavior enables you to specify a NULL *buffer* + to determine the length of the encoding. For example:: + + size_t size = json_dumpb(json, NULL, 0, 0); + if (size == 0) + return -1; + + char *buf = alloca(size); + + size = json_dumpb(json, buf, size, 0); + + .. versionadded:: 2.10 + +.. function:: int json_dumpf(const json_t *json, FILE *output, size_t flags) + + Write the JSON representation of *json* to the stream *output*. + *flags* is described above. Returns 0 on success and -1 on error. + If an error occurs, something may have already been written to + *output*. In this case, the output is undefined and most likely not + valid JSON. + +.. function:: int json_dumpfd(const json_t *json, int output, size_t flags) + + Write the JSON representation of *json* to the stream *output*. + *flags* is described above. Returns 0 on success and -1 on error. + If an error occurs, something may have already been written to + *output*. In this case, the output is undefined and most likely not + valid JSON. + + It is important to note that this function can only succeed on stream + file descriptors (such as SOCK_STREAM). Using this function on a + non-stream file descriptor will result in undefined behavior. For + non-stream file descriptors, see instead :func:`json_dumpb()`. + + This function requires POSIX and fails on all non-POSIX systems. + + .. versionadded:: 2.10 + +.. function:: int json_dump_file(const json_t *json, const char *path, size_t flags) + + Write the JSON representation of *json* to the file *path*. If + *path* already exists, it is overwritten. *flags* is described + above. Returns 0 on success and -1 on error. + +.. type:: json_dump_callback_t + + A typedef for a function that's called by + :func:`json_dump_callback()`:: + + typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); + + *buffer* points to a buffer containing a chunk of output, *size* is + the length of the buffer, and *data* is the corresponding + :func:`json_dump_callback()` argument passed through. + + *buffer* is guaranteed to be a valid UTF-8 string (i.e. multi-byte + code unit sequences are preserved). *buffer* never contains + embedded null bytes. + + On error, the function should return -1 to stop the encoding + process. On success, it should return 0. + + .. versionadded:: 2.2 + +.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) + + Call *callback* repeatedly, passing a chunk of the JSON + representation of *json* each time. *flags* is described above. + Returns 0 on success and -1 on error. + + .. versionadded:: 2.2 + + +.. _apiref-decoding: + +Decoding +======== + +This section describes the functions that can be used to decode JSON +text to the Jansson representation of JSON data. The JSON +specification requires that a JSON text is either a serialized array +or object, and this requirement is also enforced with the following +functions. In other words, the top level value in the JSON text being +decoded must be either array or object. To decode any JSON value, use +the ``JSON_DECODE_ANY`` flag (see below). + +See :ref:`rfc-conformance` for a discussion on Jansson's conformance +to the JSON specification. It explains many design decisions that +affect especially the behavior of the decoder. + +Each function takes a *flags* parameter that can be used to control +the behavior of the decoder. Its default value is 0. The following +macros can be ORed together to obtain *flags*. + +``JSON_REJECT_DUPLICATES`` + Issue a decoding error if any JSON object in the input text + contains duplicate keys. Without this flag, the value of the last + occurrence of each key ends up in the result. Key equivalence is + checked byte-by-byte, without special Unicode comparison + algorithms. + + .. versionadded:: 2.1 + +``JSON_DECODE_ANY`` + By default, the decoder expects an array or object as the input. + With this flag enabled, the decoder accepts any valid JSON value. + + **Note:** Decoding any value may be useful in some scenarios, but + it's generally discouraged as it violates strict compatibility with + :rfc:`4627`. If you use this flag, don't expect interoperability + with other JSON systems. + + .. versionadded:: 2.3 + +``JSON_DISABLE_EOF_CHECK`` + By default, the decoder expects that its whole input constitutes a + valid JSON text, and issues an error if there's extra data after + the otherwise valid JSON input. With this flag enabled, the decoder + stops after decoding a valid JSON array or object, and thus allows + extra data after the JSON text. + + Normally, reading will stop when the last ``]`` or ``}`` in the + JSON input is encountered. If both ``JSON_DISABLE_EOF_CHECK`` and + ``JSON_DECODE_ANY`` flags are used, the decoder may read one extra + UTF-8 code unit (up to 4 bytes of input). For example, decoding + ``4true`` correctly decodes the integer 4, but also reads the + ``t``. For this reason, if reading multiple consecutive values that + are not arrays or objects, they should be separated by at least one + whitespace character. + + .. versionadded:: 2.1 + +``JSON_DECODE_INT_AS_REAL`` + JSON defines only one number type. Jansson distinguishes between + ints and reals. For more information see :ref:`real-vs-integer`. + With this flag enabled the decoder interprets all numbers as real + values. Integers that do not have an exact double representation + will silently result in a loss of precision. Integers that cause + a double overflow will cause an error. + + .. versionadded:: 2.5 + +``JSON_ALLOW_NUL`` + Allow ``\u0000`` escape inside string values. This is a safety + measure; If you know your input can contain null bytes, use this + flag. If you don't use this flag, you don't have to worry about null + bytes inside strings unless you explicitly create themselves by + using e.g. :func:`json_stringn()` or ``s#`` format specifier for + :func:`json_pack()`. + + Object keys cannot have embedded null bytes even if this flag is + used. + + .. versionadded:: 2.6 + +Each function also takes an optional :type:`json_error_t` parameter +that is filled with error information if decoding fails. It's also +updated on success; the number of bytes of input read is written to +its ``position`` field. This is especially useful when using +``JSON_DISABLE_EOF_CHECK`` to read multiple consecutive JSON texts. + +.. versionadded:: 2.3 + Number of bytes of input read is written to the ``position`` field + of the :type:`json_error_t` structure. + +If no error or position information is needed, you can pass *NULL*. + +.. function:: json_t *json_loads(const char *input, size_t flags, json_error_t *error) + + .. refcounting:: new + + Decodes the JSON string *input* and returns the array or object it + contains, or *NULL* on error, in which case *error* is filled with + information about the error. *flags* is described above. + +.. function:: json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) + + .. refcounting:: new + + Decodes the JSON string *buffer*, whose length is *buflen*, and + returns the array or object it contains, or *NULL* on error, in + which case *error* is filled with information about the error. This + is similar to :func:`json_loads()` except that the string doesn't + need to be null-terminated. *flags* is described above. + + .. versionadded:: 2.1 + +.. function:: json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) + + .. refcounting:: new + + Decodes the JSON text in stream *input* and returns the array or + object it contains, or *NULL* on error, in which case *error* is + filled with information about the error. *flags* is described + above. + + This function will start reading the input from whatever position + the input file was in, without attempting to seek first. If an error + occurs, the file position will be left indeterminate. On success, + the file position will be at EOF, unless ``JSON_DISABLE_EOF_CHECK`` + flag was used. In this case, the file position will be at the first + character after the last ``]`` or ``}`` in the JSON input. This + allows calling :func:`json_loadf()` on the same ``FILE`` object + multiple times, if the input consists of consecutive JSON texts, + possibly separated by whitespace. + +.. function:: json_t *json_loadfd(int input, size_t flags, json_error_t *error) + + .. refcounting:: new + + Decodes the JSON text in stream *input* and returns the array or + object it contains, or *NULL* on error, in which case *error* is + filled with information about the error. *flags* is described + above. + + This function will start reading the input from whatever position + the input file descriptor was in, without attempting to seek first. + If an error occurs, the file position will be left indeterminate. + On success, the file position will be at EOF, unless + ``JSON_DISABLE_EOF_CHECK`` flag was used. In this case, the file + descriptor's position will be at the first character after the last + ``]`` or ``}`` in the JSON input. This allows calling + :func:`json_loadfd()` on the same file descriptor multiple times, + if the input consists of consecutive JSON texts, possibly separated + by whitespace. + + It is important to note that this function can only succeed on stream + file descriptors (such as SOCK_STREAM). Using this function on a + non-stream file descriptor will result in undefined behavior. For + non-stream file descriptors, see instead :func:`json_loadb()`. In + addition, please note that this function cannot be used on non-blocking + file descriptors (such as a non-blocking socket). Using this function + on non-blocking file descriptors has a high risk of data loss because + it does not support resuming. + + This function requires POSIX and fails on all non-POSIX systems. + + .. versionadded:: 2.10 + +.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error) + + .. refcounting:: new + + Decodes the JSON text in file *path* and returns the array or + object it contains, or *NULL* on error, in which case *error* is + filled with information about the error. *flags* is described + above. + +.. type:: json_load_callback_t + + A typedef for a function that's called by + :func:`json_load_callback()` to read a chunk of input data:: + + typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + + *buffer* points to a buffer of *buflen* bytes, and *data* is the + corresponding :func:`json_load_callback()` argument passed through. + + On success, the function should write at most *buflen* bytes to + *buffer*, and return the number of bytes written; a returned value + of 0 indicates that no data was produced and that the end of file + has been reached. On error, the function should return + ``(size_t)-1`` to abort the decoding process. + + In UTF-8, some code points are encoded as multi-byte sequences. The + callback function doesn't need to worry about this, as Jansson + handles it at a higher level. For example, you can safely read a + fixed number of bytes from a network connection without having to + care about code unit sequences broken apart by the chunk + boundaries. + + .. versionadded:: 2.4 + +.. function:: json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error) + + .. refcounting:: new + + Decodes the JSON text produced by repeated calls to *callback*, and + returns the array or object it contains, or *NULL* on error, in + which case *error* is filled with information about the error. + *data* is passed through to *callback* on each call. *flags* is + described above. + + .. versionadded:: 2.4 + + +.. _apiref-pack: + +Building Values +=============== + +This section describes functions that help to create, or *pack*, +complex JSON values, especially nested objects and arrays. Value +building is based on a *format string* that is used to tell the +functions about the expected arguments. + +For example, the format string ``"i"`` specifies a single integer +value, while the format string ``"[ssb]"`` or the equivalent ``"[s, s, +b]"`` specifies an array value with two strings and a boolean as its +items:: + + /* Create the JSON integer 42 */ + json_pack("i", 42); + + /* Create the JSON array ["foo", "bar", true] */ + json_pack("[ssb]", "foo", "bar", 1); + +Here's the full list of format specifiers. The type in parentheses +denotes the resulting JSON type, and the type in brackets (if any) +denotes the C type that is expected as the corresponding argument or +arguments. + +``s`` (string) [const char \*] + Convert a null terminated UTF-8 string to a JSON string. + +``s?`` (string) [const char \*] + Like ``s``, but if the argument is *NULL*, output a JSON null + value. + + .. versionadded:: 2.8 + +``s*`` (string) [const char \*] + Like ``s``, but if the argument is *NULL*, do not output any value. + This format can only be used inside an object or an array. If used + inside an object, the corresponding key is additionally suppressed + when the value is omitted. See below for an example. + + .. versionadded:: 2.11 + +``s#`` (string) [const char \*, int] + Convert a UTF-8 buffer of a given length to a JSON string. + + .. versionadded:: 2.5 + +``s%`` (string) [const char \*, size_t] + Like ``s#`` but the length argument is of type :type:`size_t`. + + .. versionadded:: 2.6 + +``+`` [const char \*] + Like ``s``, but concatenate to the previous string. Only valid + after ``s``, ``s#``, ``+`` or ``+#``. + + .. versionadded:: 2.5 + +``+#`` [const char \*, int] + Like ``s#``, but concatenate to the previous string. Only valid + after ``s``, ``s#``, ``+`` or ``+#``. + + .. versionadded:: 2.5 + +``+%`` (string) [const char \*, size_t] + Like ``+#`` but the length argument is of type :type:`size_t`. + + .. versionadded:: 2.6 + +``n`` (null) + Output a JSON null value. No argument is consumed. + +``b`` (boolean) [int] + Convert a C ``int`` to JSON boolean value. Zero is converted + to ``false`` and non-zero to ``true``. + +``i`` (integer) [int] + Convert a C ``int`` to JSON integer. + +``I`` (integer) [json_int_t] + Convert a C :type:`json_int_t` to JSON integer. + +``f`` (real) [double] + Convert a C ``double`` to JSON real. + +``o`` (any value) [json_t \*] + Output any given JSON value as-is. If the value is added to an + array or object, the reference to the value passed to ``o`` is + stolen by the container. + +``O`` (any value) [json_t \*] + Like ``o``, but the argument's reference count is incremented. + This is useful if you pack into an array or object and want to + keep the reference for the JSON value consumed by ``O`` to + yourself. + +``o?``, ``O?`` (any value) [json_t \*] + Like ``o`` and ``O``, respectively, but if the argument is + *NULL*, output a JSON null value. + + .. versionadded:: 2.8 + +``o*``, ``O*`` (any value) [json_t \*] + Like ``o`` and ``O``, respectively, but if the argument is + *NULL*, do not output any value. This format can only be used + inside an object or an array. If used inside an object, the + corresponding key is additionally suppressed. See below for an + example. + + .. versionadded:: 2.11 + +``[fmt]`` (array) + Build an array with contents from the inner format string. ``fmt`` + may contain objects and arrays, i.e. recursive value building is + supported. + +``{fmt}`` (object) + Build an object with contents from the inner format string + ``fmt``. The first, third, etc. format specifier represent a key, + and must be a string (see ``s``, ``s#``, ``+`` and ``+#`` above), + as object keys are always strings. The second, fourth, etc. format + specifier represent a value. Any value may be an object or array, + i.e. recursive value building is supported. + +Whitespace, ``:`` and ``,`` are ignored. + +.. function:: json_t *json_pack(const char *fmt, ...) + + .. refcounting:: new + + Build a new JSON value according to the format string *fmt*. For + each format specifier (except for ``{}[]n``), one or more arguments + are consumed and used to build the corresponding value. Returns + *NULL* on error. + +.. function:: json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) + json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) + + .. refcounting:: new + + Like :func:`json_pack()`, but an in the case of an error, an error + message is written to *error*, if it's not *NULL*. The *flags* + parameter is currently unused and should be set to 0. + + As only the errors in format string (and out-of-memory errors) can + be caught by the packer, these two functions are most likely only + useful for debugging format strings. + +More examples:: + + /* Build an empty JSON object */ + json_pack("{}"); + + /* Build the JSON object {"foo": 42, "bar": 7} */ + json_pack("{sisi}", "foo", 42, "bar", 7); + + /* Like above, ':', ',' and whitespace are ignored */ + json_pack("{s:i, s:i}", "foo", 42, "bar", 7); + + /* Build the JSON array [[1, 2], {"cool": true}] */ + json_pack("[[i,i],{s:b}]", 1, 2, "cool", 1); + + /* Build a string from a non-null terminated buffer */ + char buffer[4] = {'t', 'e', 's', 't'}; + json_pack("s#", buffer, 4); + + /* Concatenate strings together to build the JSON string "foobarbaz" */ + json_pack("s++", "foo", "bar", "baz"); + + /* Create an empty object or array when optional members are missing */ + json_pack("{s:s*,s:o*,s:O*}", "foo", NULL, "bar", NULL, "baz", NULL); + json_pack("[s*,o*,O*]", NULL, NULL, NULL); + + +.. _apiref-unpack: + +Parsing and Validating Values +============================= + +This section describes functions that help to validate complex values +and extract, or *unpack*, data from them. Like :ref:`building values +`, this is also based on format strings. + +While a JSON value is unpacked, the type specified in the format +string is checked to match that of the JSON value. This is the +validation part of the process. In addition to this, the unpacking +functions can also check that all items of arrays and objects are +unpacked. This check be enabled with the format specifier ``!`` or by +using the flag ``JSON_STRICT``. See below for details. + +Here's the full list of format specifiers. The type in parentheses +denotes the JSON type, and the type in brackets (if any) denotes the C +type whose address should be passed. + +``s`` (string) [const char \*] + Convert a JSON string to a pointer to a null terminated UTF-8 + string. The resulting string is extracted by using + :func:`json_string_value()` internally, so it exists as long as + there are still references to the corresponding JSON string. + +``s%`` (string) [const char \*, size_t \*] + Convert a JSON string to a pointer to a null terminated UTF-8 + string and its length. + + .. versionadded:: 2.6 + +``n`` (null) + Expect a JSON null value. Nothing is extracted. + +``b`` (boolean) [int] + Convert a JSON boolean value to a C ``int``, so that ``true`` + is converted to 1 and ``false`` to 0. + +``i`` (integer) [int] + Convert a JSON integer to C ``int``. + +``I`` (integer) [json_int_t] + Convert a JSON integer to C :type:`json_int_t`. + +``f`` (real) [double] + Convert a JSON real to C ``double``. + +``F`` (integer or real) [double] + Convert a JSON number (integer or real) to C ``double``. + +``o`` (any value) [json_t \*] + Store a JSON value with no conversion to a :type:`json_t` pointer. + +``O`` (any value) [json_t \*] + Like ``o``, but the JSON value's reference count is incremented. + Storage pointers should be initialized NULL before using unpack. + The caller is responsible for releasing all references incremented + by unpack, even when an error occurs. + +``[fmt]`` (array) + Convert each item in the JSON array according to the inner format + string. ``fmt`` may contain objects and arrays, i.e. recursive + value extraction is supported. + +``{fmt}`` (object) + Convert each item in the JSON object according to the inner format + string ``fmt``. The first, third, etc. format specifier represent + a key, and must be ``s``. The corresponding argument to unpack + functions is read as the object key. The second, fourth, etc. + format specifier represent a value and is written to the address + given as the corresponding argument. **Note** that every other + argument is read from and every other is written to. + + ``fmt`` may contain objects and arrays as values, i.e. recursive + value extraction is supported. + + .. versionadded:: 2.3 + Any ``s`` representing a key may be suffixed with a ``?`` to + make the key optional. If the key is not found, nothing is + extracted. See below for an example. + +``!`` + This special format specifier is used to enable the check that + all object and array items are accessed, on a per-value basis. It + must appear inside an array or object as the last format specifier + before the closing bracket or brace. To enable the check globally, + use the ``JSON_STRICT`` unpacking flag. + +``*`` + This special format specifier is the opposite of ``!``. If the + ``JSON_STRICT`` flag is used, ``*`` can be used to disable the + strict check on a per-value basis. It must appear inside an array + or object as the last format specifier before the closing bracket + or brace. + +Whitespace, ``:`` and ``,`` are ignored. + +.. function:: int json_unpack(json_t *root, const char *fmt, ...) + + Validate and unpack the JSON value *root* according to the format + string *fmt*. Returns 0 on success and -1 on failure. + +.. function:: int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) + int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap) + + Validate and unpack the JSON value *root* according to the format + string *fmt*. If an error occurs and *error* is not *NULL*, write + error information to *error*. *flags* can be used to control the + behaviour of the unpacker, see below for the flags. Returns 0 on + success and -1 on failure. + +.. note:: + + The first argument of all unpack functions is ``json_t *root`` + instead of ``const json_t *root``, because the use of ``O`` format + specifier causes the reference count of ``root``, or some value + reachable from ``root``, to be increased. Furthermore, the ``o`` + format specifier may be used to extract a value as-is, which allows + modifying the structure or contents of a value reachable from + ``root``. + + If the ``O`` and ``o`` format specifiers are not used, it's + perfectly safe to cast a ``const json_t *`` variable to plain + ``json_t *`` when used with these functions. + +The following unpacking flags are available: + +``JSON_STRICT`` + Enable the extra validation step checking that all object and + array items are unpacked. This is equivalent to appending the + format specifier ``!`` to the end of every array and object in the + format string. + +``JSON_VALIDATE_ONLY`` + Don't extract any data, just validate the JSON value against the + given format string. Note that object keys must still be specified + after the format string. + +Examples:: + + /* root is the JSON integer 42 */ + int myint; + json_unpack(root, "i", &myint); + assert(myint == 42); + + /* root is the JSON object {"foo": "bar", "quux": true} */ + const char *str; + int boolean; + json_unpack(root, "{s:s, s:b}", "foo", &str, "quux", &boolean); + assert(strcmp(str, "bar") == 0 && boolean == 1); + + /* root is the JSON array [[1, 2], {"baz": null} */ + json_error_t error; + json_unpack_ex(root, &error, JSON_VALIDATE_ONLY, "[[i,i], {s:n}]", "baz"); + /* returns 0 for validation success, nothing is extracted */ + + /* root is the JSON array [1, 2, 3, 4, 5] */ + int myint1, myint2; + json_unpack(root, "[ii!]", &myint1, &myint2); + /* returns -1 for failed validation */ + + /* root is an empty JSON object */ + int myint = 0, myint2 = 0, myint3 = 0; + json_unpack(root, "{s?i, s?[ii]}", + "foo", &myint1, + "bar", &myint2, &myint3); + /* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */ + + +Equality +======== + +Testing for equality of two JSON values cannot, in general, be +achieved using the ``==`` operator. Equality in the terms of the +``==`` operator states that the two :type:`json_t` pointers point to +exactly the same JSON value. However, two JSON values can be equal not +only if they are exactly the same value, but also if they have equal +"contents": + +* Two integer or real values are equal if their contained numeric + values are equal. An integer value is never equal to a real value, + though. + +* Two strings are equal if their contained UTF-8 strings are equal, + byte by byte. Unicode comparison algorithms are not implemented. + +* Two arrays are equal if they have the same number of elements and + each element in the first array is equal to the corresponding + element in the second array. + +* Two objects are equal if they have exactly the same keys and the + value for each key in the first object is equal to the value of the + corresponding key in the second object. + +* Two true, false or null values have no "contents", so they are equal + if their types are equal. (Because these values are singletons, + their equality can actually be tested with ``==``.) + +.. function:: int json_equal(json_t *value1, json_t *value2) + + Returns 1 if *value1* and *value2* are equal, as defined above. + Returns 0 if they are unequal or one or both of the pointers are + *NULL*. + + +Copying +======= + +Because of reference counting, passing JSON values around doesn't +require copying them. But sometimes a fresh copy of a JSON value is +needed. For example, if you need to modify an array, but still want to +use the original afterwards, you should take a copy of it first. + +Jansson supports two kinds of copying: shallow and deep. There is a +difference between these methods only for arrays and objects. Shallow +copying only copies the first level value (array or object) and uses +the same child values in the copied value. Deep copying makes a fresh +copy of the child values, too. Moreover, all the child values are deep +copied in a recursive fashion. + +Copying objects preserves the insertion order of keys. + +.. function:: json_t *json_copy(json_t *value) + + .. refcounting:: new + + Returns a shallow copy of *value*, or *NULL* on error. + +.. function:: json_t *json_deep_copy(const json_t *value) + + .. refcounting:: new + + Returns a deep copy of *value*, or *NULL* on error. + + +.. _apiref-custom-memory-allocation: + +Custom Memory Allocation +======================== + +By default, Jansson uses :func:`malloc()` and :func:`free()` for +memory allocation. These functions can be overridden if custom +behavior is needed. + +.. type:: json_malloc_t + + A typedef for a function pointer with :func:`malloc()`'s + signature:: + + typedef void *(*json_malloc_t)(size_t); + +.. type:: json_free_t + + A typedef for a function pointer with :func:`free()`'s + signature:: + + typedef void (*json_free_t)(void *); + +.. function:: void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) + + Use *malloc_fn* instead of :func:`malloc()` and *free_fn* instead + of :func:`free()`. This function has to be called before any other + Jansson's API functions to ensure that all memory operations use + the same functions. + +.. function:: void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn) + + Fetch the current malloc_fn and free_fn used. Either parameter + may be NULL. + + .. versionadded:: 2.8 + +**Examples:** + +Circumvent problems with different CRT heaps on Windows by using +application's :func:`malloc()` and :func:`free()`:: + + json_set_alloc_funcs(malloc, free); + +Use the `Boehm's conservative garbage collector`_ for memory +operations:: + + json_set_alloc_funcs(GC_malloc, GC_free); + +.. _Boehm's conservative garbage collector: http://www.hboehm.info/gc/ + +Allow storing sensitive data (e.g. passwords or encryption keys) in +JSON structures by zeroing all memory when freed:: + + static void *secure_malloc(size_t size) + { + /* Store the memory area size in the beginning of the block */ + void *ptr = malloc(size + 8); + *((size_t *)ptr) = size; + return ptr + 8; + } + + static void secure_free(void *ptr) + { + size_t size; + + ptr -= 8; + size = *((size_t *)ptr); + + guaranteed_memset(ptr, 0, size + 8); + free(ptr); + } + + int main() + { + json_set_alloc_funcs(secure_malloc, secure_free); + /* ... */ + } + +For more information about the issues of storing sensitive data in +memory, see +http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html. +The page also explains the :func:`guaranteed_memset()` function used +in the example and gives a sample implementation for it. + +.. _fixed_length_keys: + +Fixed-Length keys +================= + +The Jansson API allows work with fixed-length keys. This can be useful in the following cases: + +* The key is contained inside a buffer and is not null-terminated. In this case creating a new temporary buffer is not needed. +* The key contains U+0000 inside it. + +List of API for fixed-length keys: + +* :c:func:`json_object_getn` +* :c:func:`json_object_setn` +* :c:func:`json_object_setn_nocheck` +* :c:func:`json_object_setn_new` +* :c:func:`json_object_setn_new_nocheck` +* :c:func:`json_object_deln` +* :c:func:`json_object_iter_key_len` +* :c:func:`json_object_keylen_foreach` +* :c:func:`json_object_keylen_foreach_safe` + +**Examples:** + +Try to write a new function to get :c:struct:`json_t` by path separated by ``.`` + +This requires: + +* string iterator (no need to modify the input for better performance) +* API for working with fixed-size keys + +The iterator:: + + struct string { + const char *string; + size_t length; + }; + + size_t string_try_next(struct string *str, const char *delimiter) { + str->string += strspn(str->string, delimiter); + str->length = strcspn(str->string, delimiter); + return str->length; + } + + #define string_foreach(_string, _delimiter) \ + for (; string_try_next(&(_string), _delimiter); (_string).string += (_string).length) + + +The function:: + + json_t *json_object_get_by_path(json_t *object, const char *path) { + struct string str; + json_t *out = object; + + str.string = path; + + string_foreach(str, ".") { + out = json_object_getn(out, str.string, str.length); + if (out == NULL) + return NULL; + } + + return out; + } + +And usage:: + + int main(void) { + json_t *obj = json_pack("{s:{s:{s:b}}}", "a", "b", "c", 1); + + json_t *c = json_object_get_by_path(obj, "a.b.c"); + assert(json_is_true(c)); + + json_decref(obj); + } diff --git a/lib/jansson/doc/changes.rst b/lib/jansson/doc/changes.rst new file mode 100644 index 0000000..ea56843 --- /dev/null +++ b/lib/jansson/doc/changes.rst @@ -0,0 +1,5 @@ +****************** +Changes in Jansson +****************** + +.. include:: ../CHANGES diff --git a/lib/jansson/doc/conf.py b/lib/jansson/doc/conf.py new file mode 100644 index 0000000..2426171 --- /dev/null +++ b/lib/jansson/doc/conf.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +# +# Jansson documentation build configuration file, created by +# sphinx-quickstart on Sun Sep 5 21:47:20 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('ext')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['refcounting'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Jansson' +copyright = u'2009-2020, Petri Lehtinen' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '2.14' +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +default_role = 'c:func' +primary_domain = 'c' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +#html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Janssondoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Jansson.tex', u'Jansson Documentation', + u'Petri Lehtinen', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'jansson', u'Jansson Documentation', + [u'Petri Lehtinen'], 1) +] diff --git a/lib/jansson/doc/conformance.rst b/lib/jansson/doc/conformance.rst new file mode 100644 index 0000000..5556a6b --- /dev/null +++ b/lib/jansson/doc/conformance.rst @@ -0,0 +1,119 @@ +.. _rfc-conformance: + +*************** +RFC Conformance +*************** + +JSON is specified in :rfc:`4627`, *"The application/json Media Type +for JavaScript Object Notation (JSON)"*. + +Character Encoding +================== + +Jansson only supports UTF-8 encoded JSON texts. It does not support or +auto-detect any of the other encodings mentioned in the RFC, namely +UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE. Pure ASCII is supported, as +it's a subset of UTF-8. + +Strings +======= + +JSON strings are mapped to C-style null-terminated character arrays, +and UTF-8 encoding is used internally. + +All Unicode codepoints U+0000 through U+10FFFF are allowed in string +values. However, U+0000 is allowed in object keys only for length-aware functions. + +Unicode normalization or any other transformation is never performed +on any strings (string values or object keys). When checking for +equivalence of strings or object keys, the comparison is performed +byte by byte between the original UTF-8 representations of the +strings. + +Numbers +======= + +.. _real-vs-integer: + +Real vs. Integer +---------------- + +JSON makes no distinction between real and integer numbers; Jansson +does. Real numbers are mapped to the ``double`` type and integers to +the ``json_int_t`` type, which is a typedef of ``long long`` or +``long``, depending on whether ``long long`` is supported by your +compiler or not. + +A JSON number is considered to be a real number if its lexical +representation includes one of ``e``, ``E``, or ``.``; regardless if +its actual numeric value is a true integer (e.g., all of ``1E6``, +``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but +will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL`` +decoder flag set all numbers are interpreted as real. + +All other JSON numbers are considered integers. + +When encoding to JSON, real values are always represented +with a fractional part; e.g., the ``double`` value 3.0 will be +represented in JSON as ``3.0``, not ``3``. + +Overflow, Underflow & Precision +------------------------------- + +Real numbers whose absolute values are too small to be represented in +a C ``double`` will be silently estimated with 0.0. Thus, depending on +platform, JSON numbers very close to zero such as 1E-999 may result in +0.0. + +Real numbers whose absolute values are too large to be represented in +a C ``double`` will result in an overflow error (a JSON decoding +error). Thus, depending on platform, JSON numbers like 1E+999 or +-1E+999 may result in a parsing error. + +Likewise, integer numbers whose absolute values are too large to be +represented in the ``json_int_t`` type (see above) will result in an +overflow error (a JSON decoding error). Thus, depending on platform, +JSON numbers like 1000000000000000 may result in parsing error. + +Parsing JSON real numbers may result in a loss of precision. As long +as overflow does not occur (i.e. a total loss of precision), the +rounded approximate value is silently used. Thus the JSON number +1.000000000000000005 may, depending on platform, result in the +``double`` value 1.0. + +Signed zeros +------------ + +JSON makes no statement about what a number means; however Javascript +(ECMAscript) does state that +0.0 and -0.0 must be treated as being +distinct values, i.e. -0.0 |not-equal| 0.0. Jansson relies on the +underlying floating point library in the C environment in which it is +compiled. Therefore it is platform-dependent whether 0.0 and -0.0 will +be distinct values. Most platforms that use the IEEE 754 +floating-point standard will support signed zeros. + +Note that this only applies to floating-point; neither JSON, C, or +IEEE support the concept of signed integer zeros. + +.. |not-equal| unicode:: U+2260 + +Types +----- + +No support is provided in Jansson for any C numeric types other than +``json_int_t`` and ``double``. This excludes things such as unsigned +types, ``long double``, etc. Obviously, shorter types like ``short``, +``int``, ``long`` (if ``json_int_t`` is ``long long``) and ``float`` +are implicitly handled via the ordinary C type coercion rules (subject +to overflow semantics). Also, no support or hooks are provided for any +supplemental "bignum" type add-on packages. + +Depth of nested values +====================== + +To avoid stack exhaustion, Jansson currently limits the nesting depth +for arrays and objects to a certain value (default: 2048), defined as +a macro ``JSON_PARSER_MAX_DEPTH`` within ``jansson_config.h``. + +The limit is allowed to be set by the RFC; there is no recommended value +or required minimum depth to be supported. diff --git a/lib/jansson/doc/ext/refcounting.py b/lib/jansson/doc/ext/refcounting.py new file mode 100644 index 0000000..e72c481 --- /dev/null +++ b/lib/jansson/doc/ext/refcounting.py @@ -0,0 +1,69 @@ +""" + refcounting + ~~~~~~~~~~~ + + Reference count annotations for C API functions. Has the same + result as the sphinx.ext.refcounting extension but works for all + functions regardless of the signature, and the reference counting + information is written inline with the documentation instead of a + separate file. + + Adds a new directive "refcounting". The directive has no content + and one required positional parameter:: "new" or "borrow". + + Example: + + .. cfunction:: json_t *json_object(void) + + .. refcounting:: new + + + + :copyright: Copyright (c) 2009-2016 Petri Lehtinen + :license: MIT, see LICENSE for details. +""" + +from docutils import nodes +from docutils.parsers.rst import Directive + + +def visit(self, node): + self.visit_emphasis(node) + +def depart(self, node): + self.depart_emphasis(node) + +def html_visit(self, node): + self.body.append(self.starttag(node, 'em', '', CLASS='refcount')) + +def html_depart(self, node): + self.body.append('') + + +class refcounting(nodes.emphasis): + pass + +class refcounting_directive(Directive): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + + def run(self): + if self.arguments[0] == 'borrow': + text = 'Return value: Borrowed reference.' + elif self.arguments[0] == 'new': + text = 'Return value: New reference.' + else: + raise Error('Valid arguments: new, borrow') + + return [refcounting(text, text)] + + +def setup(app): + app.add_node(refcounting, + html=(html_visit, html_depart), + latex=(visit, depart), + text=(visit, depart), + man=(visit, depart)) + app.add_directive('refcounting', refcounting_directive) diff --git a/lib/jansson/doc/gettingstarted.rst b/lib/jansson/doc/gettingstarted.rst new file mode 100644 index 0000000..4cd1977 --- /dev/null +++ b/lib/jansson/doc/gettingstarted.rst @@ -0,0 +1,264 @@ +*************** +Getting Started +*************** + +.. highlight:: c + +Compiling and Installing Jansson +================================ + +The Jansson source is available at +http://www.digip.org/jansson/releases/. + +Unix-like systems (including MinGW) +----------------------------------- + +Unpack the source tarball and change to the source directory: + +.. parsed-literal:: + + bunzip2 -c jansson-|release|.tar.bz2 | tar xf - + cd jansson-|release| + +The source uses GNU Autotools (autoconf_, automake_, libtool_), so +compiling and installing is extremely simple:: + + ./configure + make + make check + make install + +To change the destination directory (``/usr/local`` by default), use +the ``--prefix=DIR`` argument to ``./configure``. See ``./configure +--help`` for the list of all possible configuration options. + +The command ``make check`` runs the test suite distributed with +Jansson. This step is not strictly necessary, but it may find possible +problems that Jansson has on your platform. If any problems are found, +please report them. + +If you obtained the source from a Git repository (or any other source +control system), there's no ``./configure`` script as it's not kept in +version control. To create the script, the build system needs to be +bootstrapped. There are many ways to do this, but the easiest one is +to use ``autoreconf``:: + + autoreconf -fi + +This command creates the ``./configure`` script, which can then be +used as described above. + +.. _autoconf: http://www.gnu.org/software/autoconf/ +.. _automake: http://www.gnu.org/software/automake/ +.. _libtool: http://www.gnu.org/software/libtool/ + + +.. _build-cmake: + +CMake (various platforms, including Windows) +-------------------------------------------- + +Jansson can be built using CMake_. Create a build directory for an +out-of-tree build, change to that directory, and run ``cmake`` (or ``ccmake``, +``cmake-gui``, or similar) to configure the project. + +See the examples below for more detailed information. + +.. note:: In the below examples ``..`` is used as an argument for ``cmake``. + This is simply the path to the jansson project root directory. + In the example it is assumed you've created a sub-directory ``build`` + and are using that. You could use any path you want. + +.. _build-cmake-unix: + +Unix (Make files) +^^^^^^^^^^^^^^^^^ +Generating make files on unix: + +.. parsed-literal:: + + bunzip2 -c jansson-|release|.tar.bz2 | tar xf - + cd jansson-|release| + + mkdir build + cd build + cmake .. # or ccmake .. for a GUI. + +.. note:: + + If you don't want to build docs or ``Sphinx`` is not installed, you should add ``"-DJANSSON_BUILD_DOCS=OFF"`` in the ``cmake`` command. + + +Then to build:: + + make + make check + make install + +Windows (Visual Studio) +^^^^^^^^^^^^^^^^^^^^^^^ +Creating Visual Studio project files from the command line: + +.. parsed-literal:: + + + cd jansson-|release| + + md build + cd build + cmake -G "Visual Studio 15 2017" .. + +.. note:: + + You should replace the name of the generator (``-G`` flag) matching + the Visual Studio version installed on your system. Currently, the + following versions are supported: + + - ``Visual Studio 9 2008`` + - ``Visual Studio 10 2010`` + - ``Visual Studio 11 2012`` + - ``Visual Studio 12 2013`` + - ``Visual Studio 14 2015`` + - ``Visual Studio 15 2017`` + - ``Visual Studio 16 2019`` + + Any later version should also work. + +You will now have a *Visual Studio Solution* in your build directory. +To run the unit tests build the ``RUN_TESTS`` project. + +If you prefer a GUI the ``cmake`` line in the above example can +be replaced with:: + + cmake-gui .. + +For command line help (including a list of available generators) +for CMake_ simply run:: + + cmake + +To list available CMake_ settings (and what they are currently set to) +for the project, run:: + + cmake -LH .. + +Windows (MinGW) +^^^^^^^^^^^^^^^ +If you prefer using MinGW on Windows, make sure MinGW installed and ``{MinGW}/bin`` has been added to ``PATH``, then do the following commands: + +.. parsed-literal:: + + + cd jansson-|release| + + md build + cd build + cmake -G "MinGW Makefiles" .. + mingw32-make + + +Mac OSX (Xcode) +^^^^^^^^^^^^^^^ +If you prefer using Xcode instead of make files on OSX, +do the following. (Use the same steps as +for :ref:`Unix `):: + + ... + cmake -G "Xcode" .. + +Additional CMake settings +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Shared library +"""""""""""""" +By default the CMake_ project will generate build files for building the +static library. To build the shared version use:: + + ... + cmake -DJANSSON_BUILD_SHARED_LIBS=1 .. + +Changing install directory (same as autoconf --prefix) +"""""""""""""""""""""""""""""""""""""""""""""""""""""" +Just as with the autoconf_ project you can change the destination directory +for ``make install``. The equivalent for autoconfs ``./configure --prefix`` +in CMake_ is:: + + ... + cmake -DCMAKE_INSTALL_PREFIX:PATH=/some/other/path .. + make install + +.. _CMake: http://www.cmake.org + + +Android +------- + +Jansson can be built for Android platforms. Android.mk is in the +source root directory. The configuration header file is located in the +``android`` directory in the source distribution. + + +Other Systems +------------- + +On non Unix-like systems, you may be unable to run the ``./configure`` +script. In this case, follow these steps. All the files mentioned can +be found in the ``src/`` directory. + +1. Create ``jansson_config.h`` (which has some platform-specific + parameters that are normally filled in by the ``./configure`` + script). Edit ``jansson_config.h.in``, replacing all ``@variable@`` + placeholders, and rename the file to ``jansson_config.h``. + +2. Make ``jansson.h`` and ``jansson_config.h`` available to the + compiler, so that they can be found when compiling programs that + use Jansson. + +3. Compile all the ``.c`` files (in the ``src/`` directory) into a + library file. Make the library available to the compiler, as in + step 2. + + +Building the Documentation +-------------------------- + +(This subsection describes how to build the HTML documentation you are +currently reading, so it can be safely skipped.) + +Documentation is in the ``doc/`` subdirectory. It's written in +reStructuredText_ with Sphinx_ annotations. To generate the HTML +documentation, invoke:: + + make html + +and point your browser to ``doc/_build/html/index.html``. Sphinx_ 1.0 +or newer is required to generate the documentation. + +.. _reStructuredText: http://docutils.sourceforge.net/rst.html +.. _Sphinx: http://sphinx.pocoo.org/ + + +Compiling Programs that Use Jansson +=================================== + +Jansson involves one C header file, :file:`jansson.h`, so it's enough +to put the line + +:: + + #include + +in the beginning of every source file that uses Jansson. + +There's also just one library to link with, ``libjansson``. Compile and +link the program as follows:: + + cc -o prog prog.c -ljansson + +Starting from version 1.2, there's also support for pkg-config_: + +.. code-block:: shell + + cc -o prog prog.c `pkg-config --cflags --libs jansson` + +.. _pkg-config: http://pkg-config.freedesktop.org/ diff --git a/lib/jansson/doc/github_commits.c b/lib/jansson/doc/github_commits.c new file mode 100644 index 0000000..81c8063 --- /dev/null +++ b/lib/jansson/doc/github_commits.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include + +#include +#include + +#define BUFFER_SIZE (256 * 1024) /* 256 KB */ + +#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits" +#define URL_SIZE 256 + +/* Return the offset of the first newline in text or the length of + text if there's no newline */ +static int newline_offset(const char *text) { + const char *newline = strchr(text, '\n'); + if (!newline) + return strlen(text); + else + return (int)(newline - text); +} + +struct write_result { + char *data; + int pos; +}; + +static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) { + struct write_result *result = (struct write_result *)stream; + + if (result->pos + size * nmemb >= BUFFER_SIZE - 1) { + fprintf(stderr, "error: too small buffer\n"); + return 0; + } + + memcpy(result->data + result->pos, ptr, size * nmemb); + result->pos += size * nmemb; + + return size * nmemb; +} + +static char *request(const char *url) { + CURL *curl = NULL; + CURLcode status; + struct curl_slist *headers = NULL; + char *data = NULL; + long code; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if (!curl) + goto error; + + data = malloc(BUFFER_SIZE); + if (!data) + goto error; + + struct write_result write_result = {.data = data, .pos = 0}; + + curl_easy_setopt(curl, CURLOPT_URL, url); + + /* GitHub commits API v3 requires a User-Agent header */ + headers = curl_slist_append(headers, "User-Agent: Jansson-Tutorial"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); + + status = curl_easy_perform(curl); + if (status != 0) { + fprintf(stderr, "error: unable to request data from %s:\n", url); + fprintf(stderr, "%s\n", curl_easy_strerror(status)); + goto error; + } + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code != 200) { + fprintf(stderr, "error: server responded with code %ld\n", code); + goto error; + } + + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + curl_global_cleanup(); + + /* zero-terminate the result */ + data[write_result.pos] = '\0'; + + return data; + +error: + if (data) + free(data); + if (curl) + curl_easy_cleanup(curl); + if (headers) + curl_slist_free_all(headers); + curl_global_cleanup(); + return NULL; +} + +int main(int argc, char *argv[]) { + size_t i; + char *text; + char url[URL_SIZE]; + + json_t *root; + json_error_t error; + + if (argc != 3) { + fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]); + fprintf(stderr, "List commits at USER's REPOSITORY.\n\n"); + return 2; + } + + snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]); + + text = request(url); + if (!text) + return 1; + + root = json_loads(text, 0, &error); + free(text); + + if (!root) { + fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); + return 1; + } + + if (!json_is_array(root)) { + fprintf(stderr, "error: root is not an array\n"); + json_decref(root); + return 1; + } + + for (i = 0; i < json_array_size(root); i++) { + json_t *data, *sha, *commit, *message; + const char *message_text; + + data = json_array_get(root, i); + if (!json_is_object(data)) { + fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1)); + json_decref(root); + return 1; + } + + sha = json_object_get(data, "sha"); + if (!json_is_string(sha)) { + fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1)); + json_decref(root); + return 1; + } + + commit = json_object_get(data, "commit"); + if (!json_is_object(commit)) { + fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1)); + json_decref(root); + return 1; + } + + message = json_object_get(commit, "message"); + if (!json_is_string(message)) { + fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1)); + json_decref(root); + return 1; + } + + message_text = json_string_value(message); + printf("%.8s %.*s\n", json_string_value(sha), newline_offset(message_text), + message_text); + } + + json_decref(root); + return 0; +} diff --git a/lib/jansson/doc/index.rst b/lib/jansson/doc/index.rst new file mode 100644 index 0000000..c679f40 --- /dev/null +++ b/lib/jansson/doc/index.rst @@ -0,0 +1,53 @@ +Jansson Documentation +===================== + +This is the documentation for Jansson_ |release|, last updated |today|. + +Introduction +------------ + +Jansson_ is a C library for encoding, decoding and manipulating JSON +data. Its main features and design principles are: + +- Simple and intuitive API and data model + +- Comprehensive documentation + +- No dependencies on other libraries + +- Full Unicode support (UTF-8) + +- Extensive test suite + +Jansson is licensed under the `MIT license`_; see LICENSE in the +source distribution for details. + +Jansson is used in production and its API is stable. It works on +numerous platforms, including numerous Unix like systems and Windows. +It's suitable for use on any system, including desktop, server, and +small embedded systems. + + +.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php +.. _Jansson: http://www.digip.org/jansson/ + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + gettingstarted + upgrading + tutorial + conformance + threadsafety + apiref + changes + + +Indices and Tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/lib/jansson/doc/threadsafety.rst b/lib/jansson/doc/threadsafety.rst new file mode 100644 index 0000000..0eebb29 --- /dev/null +++ b/lib/jansson/doc/threadsafety.rst @@ -0,0 +1,82 @@ +.. _thread-safety: + +************* +Thread safety +************* + +Jansson as a library is thread safe and has no mutable global state. +The only exceptions are the hash function seed and memory allocation +functions, see below. + +There's no locking performed inside Jansson's code. **Read-only** +access to JSON values shared by multiple threads is safe, but +**mutating** a JSON value that's shared by multiple threads is not. A +multithreaded program must perform its own locking if JSON values +shared by multiple threads are mutated. + +However, **reference count manipulation** (:func:`json_incref()`, +:func:`json_decref()`) is usually thread-safe, and can be performed on +JSON values that are shared among threads. The thread-safety of +reference counting can be checked with the +``JANSSON_THREAD_SAFE_REFCOUNT`` preprocessor constant. Thread-safe +reference count manipulation is achieved using compiler built-in +atomic functions, which are available in most modern compilers. + +If compiler support is not available (``JANSSON_THREAD_SAFE_REFCOUNT`` +is not defined), it may be very difficult to ensure thread safety of +reference counting. It's possible to have a reference to a value +that's also stored inside an array or object in another thread. +Modifying the container (adding or removing values) may trigger +concurrent access to such values, as containers manage the reference +count of their contained values. + + +Hash function seed +================== + +To prevent an attacker from intentionally causing large JSON objects +with specially crafted keys to perform very slow, the hash function +used by Jansson is randomized using a seed value. The seed is +automatically generated on the first explicit or implicit call to +:func:`json_object()`, if :func:`json_object_seed()` has not been +called beforehand. + +The seed is generated by using operating system's entropy sources if +they are available (``/dev/urandom``, ``CryptGenRandom()``). The +initialization is done in as thread safe manner as possible, by using +architecture specific lockless operations if provided by the platform +or the compiler. + +If you're using threads, it's recommended to autoseed the hashtable +explicitly before spawning any threads by calling +``json_object_seed(0)`` , especially if you're unsure whether the +initialization is thread safe on your platform. + + +Memory allocation functions +=========================== + +Memory allocation functions should be set at most once, and only on +program startup. See :ref:`apiref-custom-memory-allocation`. + + +Locale +====== + +Jansson works fine under any locale. + +However, if the host program is multithreaded and uses ``setlocale()`` +to switch the locale in one thread while Jansson is currently encoding +or decoding JSON data in another thread, the result may be wrong or +the program may even crash. + +Jansson uses locale specific functions for certain string conversions +in the encoder and decoder, and then converts the locale specific +values to/from the JSON representation. This fails if the locale +changes between the string conversion and the locale-to-JSON +conversion. This can only happen in multithreaded programs that use +``setlocale()``, because ``setlocale()`` switches the locale for all +running threads, not only the thread that calls ``setlocale()``. + +If your program uses ``setlocale()`` as described above, consider +using the thread-safe ``uselocale()`` instead. diff --git a/lib/jansson/doc/tutorial.rst b/lib/jansson/doc/tutorial.rst new file mode 100644 index 0000000..bb7a6c2 --- /dev/null +++ b/lib/jansson/doc/tutorial.rst @@ -0,0 +1,288 @@ +.. _tutorial: + +******** +Tutorial +******** + +.. highlight:: c + +In this tutorial, we create a program that fetches the latest commits +of a repository in GitHub_ over the web. `GitHub API`_ uses JSON, so +the result can be parsed using Jansson. + +To stick to the scope of this tutorial, we will only cover the +parts of the program related to handling JSON data. For the best user +experience, the full source code is available: +:download:`github_commits.c`. To compile it (on Unix-like systems with +gcc), use the following command:: + + gcc -o github_commits github_commits.c -ljansson -lcurl + +libcurl_ is used to communicate over the web, so it is required to +compile the program. + +The command line syntax is:: + + github_commits USER REPOSITORY + +``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository +name. Please note that the GitHub API is rate limited, so if you run +the program too many times within a short period of time, the sever +starts to respond with an error. + +.. _GitHub: https://github.com/ +.. _GitHub API: http://developer.github.com/ +.. _libcurl: http://curl.haxx.se/ + + +.. _tutorial-github-commits-api: + +The GitHub Repo Commits API +=========================== + +The `GitHub Repo Commits API`_ is used by sending HTTP requests to +URLs like ``https://api.github.com/repos/USER/REPOSITORY/commits``, +where ``USER`` and ``REPOSITORY`` are the GitHub user ID and the name +of the repository whose commits are to be listed, respectively. + +GitHub responds with a JSON array of the following form: + +.. code-block:: none + + [ + { + "sha": "", + "commit": { + "message": "", + + }, + + }, + { + "sha": "", + "commit": { + "message": "", + + }, + + }, + + ] + +In our program, the HTTP request is sent using the following +function:: + + static char *request(const char *url); + +It takes the URL as a parameter, performs a HTTP GET request, and +returns a newly allocated string that contains the response body. If +the request fails, an error message is printed to stderr and the +return value is *NULL*. For full details, refer to :download:`the code +`, as the actual implementation is not important +here. + +.. _GitHub Repo Commits API: http://developer.github.com/v3/repos/commits/ + +.. _tutorial-the-program: + +The Program +=========== + +First the includes:: + + #include + #include + +Like all the programs using Jansson, we need to include +:file:`jansson.h`. + +The following definitions are used to build the GitHub API request +URL:: + + #define URL_FORMAT "https://api.github.com/repos/%s/%s/commits" + #define URL_SIZE 256 + +The following function is used when formatting the result to find the +first newline in the commit message:: + + /* Return the offset of the first newline in text or the length of + text if there's no newline */ + static int newline_offset(const char *text) + { + const char *newline = strchr(text, '\n'); + if(!newline) + return strlen(text); + else + return (int)(newline - text); + } + +The main function follows. In the beginning, we first declare a bunch +of variables and check the command line parameters:: + + int main(int argc, char *argv[]) + { + size_t i; + char *text; + char url[URL_SIZE]; + + json_t *root; + json_error_t error; + + if(argc != 3) + { + fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]); + fprintf(stderr, "List commits at USER's REPOSITORY.\n\n"); + return 2; + } + +Then we build the request URL using the user and repository names +given as command line parameters:: + + snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]); + +This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above. +Now we're ready to actually request the JSON data over the web:: + + text = request(url); + if(!text) + return 1; + +If an error occurs, our function ``request`` prints the error and +returns *NULL*, so it's enough to just return 1 from the main +function. + +Next we'll call :func:`json_loads()` to decode the JSON text we got +as a response:: + + root = json_loads(text, 0, &error); + free(text); + + if(!root) + { + fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); + return 1; + } + +We don't need the JSON text anymore, so we can free the ``text`` +variable right after decoding it. If :func:`json_loads()` fails, it +returns *NULL* and sets error information to the :type:`json_error_t` +structure given as the second parameter. In this case, our program +prints the error information out and returns 1 from the main function. + +Now we're ready to extract the data out of the decoded JSON response. +The structure of the response JSON was explained in section +:ref:`tutorial-github-commits-api`. + +We check that the returned value really is an array:: + + if(!json_is_array(root)) + { + fprintf(stderr, "error: root is not an array\n"); + json_decref(root); + return 1; + } + +Then we proceed to loop over all the commits in the array:: + + for(i = 0; i < json_array_size(root); i++) + { + json_t *data, *sha, *commit, *message; + const char *message_text; + + data = json_array_get(root, i); + if(!json_is_object(data)) + { + fprintf(stderr, "error: commit data %d is not an object\n", i + 1); + json_decref(root); + return 1; + } + ... + +The function :func:`json_array_size()` returns the size of a JSON +array. First, we again declare some variables and then extract the +i'th element of the ``root`` array using :func:`json_array_get()`. +We also check that the resulting value is a JSON object. + +Next we'll extract the commit ID (a hexadecimal SHA-1 sum), +intermediate commit info object, and the commit message from that +object. We also do proper type checks:: + + sha = json_object_get(data, "sha"); + if(!json_is_string(sha)) + { + fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1); + json_decref(root); + return 1; + } + + commit = json_object_get(data, "commit"); + if(!json_is_object(commit)) + { + fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1); + json_decref(root); + return 1; + } + + message = json_object_get(commit, "message"); + if(!json_is_string(message)) + { + fprintf(stderr, "error: commit %d: message is not a string\n", i + 1); + json_decref(root); + return 1; + } + ... + +And finally, we'll print the first 8 characters of the commit ID and +the first line of the commit message. A C-style string is extracted +from a JSON string using :func:`json_string_value()`:: + + message_text = json_string_value(message); + printf("%.8s %.*s\n", + json_string_value(sha), + newline_offset(message_text), + message_text); + } + +After sending the HTTP request, we decoded the JSON text using +:func:`json_loads()`, remember? It returns a *new reference* to the +JSON value it decodes. When we're finished with the value, we'll need +to decrease the reference count using :func:`json_decref()`. This way +Jansson can release the resources:: + + json_decref(root); + return 0; + +For a detailed explanation of reference counting in Jansson, see +:ref:`apiref-reference-count` in :ref:`apiref`. + +The program's ready, let's test it and view the latest commits in +Jansson's repository: + +.. code-block:: shell + + $ ./github_commits akheron jansson + 1581f26a Merge branch '2.3' + aabfd493 load: Change buffer_pos to be a size_t + bd72efbd load: Avoid unexpected behaviour in macro expansion + e8fd3e30 Document and tweak json_load_callback() + 873eddaf Merge pull request #60 from rogerz/contrib + bd2c0c73 Ignore the binary test_load_callback + 17a51a4b Merge branch '2.3' + 09c39adc Add json_load_callback to the list of exported symbols + cbb80baf Merge pull request #57 from rogerz/contrib + 040bd7b0 Add json_load_callback() + 2637faa4 Make test stripping locale independent + <...> + + +Conclusion +========== + +In this tutorial, we implemented a program that fetches the latest +commits of a GitHub repository using the GitHub Repo Commits API. +Jansson was used to decode the JSON response and to extract the commit +data. + +This tutorial only covered a small part of Jansson. For example, we +did not create or manipulate JSON values at all. Proceed to +:ref:`apiref` to explore all features of Jansson. diff --git a/lib/jansson/doc/upgrading.rst b/lib/jansson/doc/upgrading.rst new file mode 100644 index 0000000..94ff7de --- /dev/null +++ b/lib/jansson/doc/upgrading.rst @@ -0,0 +1,76 @@ +.. highlight:: c + +****************** +Upgrading from 1.x +****************** + +This chapter lists the backwards incompatible changes introduced in +Jansson 2.0, and the steps that are needed for upgrading your code. + +**The incompatibilities are not dramatic.** The biggest change is that +all decoding functions now require and extra parameter. Most programs +can be modified to work with 2.0 by adding a ``0`` as the second +parameter to all calls of :func:`json_loads()`, :func:`json_loadf()` +and :func:`json_load_file()`. + + +Compatibility +============= + +Jansson 2.0 is backwards incompatible with the Jansson 1.x releases. +It is ABI incompatible, i.e. all programs dynamically linking to the +Jansson library need to be recompiled. It's also API incompatible, +i.e. the source code of programs using Jansson 1.x may need +modifications to make them compile against Jansson 2.0. + +All the 2.x releases are guaranteed to be backwards compatible for +both ABI and API, so no recompilation or source changes are needed +when upgrading from 2.x to 2.y. + + +List of Incompatible Changes +============================ + +**Decoding flags** + For future needs, a ``flags`` parameter was added as the second + parameter to all decoding functions, i.e. :func:`json_loads()`, + :func:`json_loadf()` and :func:`json_load_file()`. All calls to + these functions need to be changed by adding a ``0`` as the second + argument. For example:: + + /* old code */ + json_loads(input, &error); + + /* new code */ + json_loads(input, 0, &error); + + +**Underlying type of JSON integers** + The underlying C type of JSON integers has been changed from + ``int`` to the widest available signed integer type, i.e. + ``long long`` or ``long``, depending on whether + ``long long`` is supported on your system or not. This makes + the whole 64-bit integer range available on most modern systems. + + ``jansson.h`` has a typedef :type:`json_int_t` to the underlying + integer type. ``int`` should still be used in most cases when + dealing with smallish JSON integers, as the compiler handles + implicit type coercion. Only when the full 64-bit range is needed, + :type:`json_int_t` should be explicitly used. + + +**Maximum encoder indentation depth** + The maximum argument of the ``JSON_INDENT()`` macro has been + changed from 255 to 31, to free up bits from the ``flags`` + parameter of :func:`json_dumps()`, :func:`json_dumpf()` and + :func:`json_dump_file()`. If your code uses a bigger indentation + than 31, it needs to be changed. + + +**Unsigned integers in API functions** + Version 2.0 unifies unsigned integer usage in the API. All uses of + ``unsigned int`` and ``unsigned long`` have been replaced + with ``size_t``. This includes flags, container sizes, etc. + This should not require source code changes, as both + ``unsigned int`` and ``unsigned long`` are usually + compatible with ``size_t``. diff --git a/lib/jansson/examples/README.rst b/lib/jansson/examples/README.rst new file mode 100644 index 0000000..a7c5274 --- /dev/null +++ b/lib/jansson/examples/README.rst @@ -0,0 +1,4 @@ +Jansson examples +================ + +This directory contains simple example programs that use Jansson. diff --git a/lib/jansson/examples/simple_parse.c b/lib/jansson/examples/simple_parse.c new file mode 100644 index 0000000..a96a0f8 --- /dev/null +++ b/lib/jansson/examples/simple_parse.c @@ -0,0 +1,200 @@ +/* + * Simple example of parsing and printing JSON using jansson. + * + * SYNOPSIS: + * $ examples/simple_parse + * Type some JSON > [true, false, null, 1, 0.0, -0.0, "", {"name": "barney"}] + * JSON Array of 8 elements: + * JSON True + * JSON False + * JSON Null + * JSON Integer: "1" + * JSON Real: 0.000000 + * JSON Real: -0.000000 + * JSON String: "" + * JSON Object of 1 pair: + * JSON Key: "name" + * JSON String: "barney" + * + * Copyright (c) 2014 Robert Poor + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include + +/* forward refs */ +void print_json(json_t *root); +void print_json_aux(json_t *element, int indent); +void print_json_indent(int indent); +const char *json_plural(size_t count); +void print_json_object(json_t *element, int indent); +void print_json_array(json_t *element, int indent); +void print_json_string(json_t *element, int indent); +void print_json_integer(json_t *element, int indent); +void print_json_real(json_t *element, int indent); +void print_json_true(json_t *element, int indent); +void print_json_false(json_t *element, int indent); +void print_json_null(json_t *element, int indent); + +void print_json(json_t *root) { print_json_aux(root, 0); } + +void print_json_aux(json_t *element, int indent) { + switch (json_typeof(element)) { + case JSON_OBJECT: + print_json_object(element, indent); + break; + case JSON_ARRAY: + print_json_array(element, indent); + break; + case JSON_STRING: + print_json_string(element, indent); + break; + case JSON_INTEGER: + print_json_integer(element, indent); + break; + case JSON_REAL: + print_json_real(element, indent); + break; + case JSON_TRUE: + print_json_true(element, indent); + break; + case JSON_FALSE: + print_json_false(element, indent); + break; + case JSON_NULL: + print_json_null(element, indent); + break; + default: + fprintf(stderr, "unrecognized JSON type %d\n", json_typeof(element)); + } +} + +void print_json_indent(int indent) { + int i; + for (i = 0; i < indent; i++) { + putchar(' '); + } +} + +const char *json_plural(size_t count) { return count == 1 ? "" : "s"; } + +void print_json_object(json_t *element, int indent) { + size_t size; + const char *key; + json_t *value; + + print_json_indent(indent); + size = json_object_size(element); + + printf("JSON Object of %lld pair%s:\n", (long long)size, json_plural(size)); + json_object_foreach(element, key, value) { + print_json_indent(indent + 2); + printf("JSON Key: \"%s\"\n", key); + print_json_aux(value, indent + 2); + } +} + +void print_json_array(json_t *element, int indent) { + size_t i; + size_t size = json_array_size(element); + print_json_indent(indent); + + printf("JSON Array of %lld element%s:\n", (long long)size, json_plural(size)); + for (i = 0; i < size; i++) { + print_json_aux(json_array_get(element, i), indent + 2); + } +} + +void print_json_string(json_t *element, int indent) { + print_json_indent(indent); + printf("JSON String: \"%s\"\n", json_string_value(element)); +} + +void print_json_integer(json_t *element, int indent) { + print_json_indent(indent); + printf("JSON Integer: \"%" JSON_INTEGER_FORMAT "\"\n", json_integer_value(element)); +} + +void print_json_real(json_t *element, int indent) { + print_json_indent(indent); + printf("JSON Real: %f\n", json_real_value(element)); +} + +void print_json_true(json_t *element, int indent) { + (void)element; + print_json_indent(indent); + printf("JSON True\n"); +} + +void print_json_false(json_t *element, int indent) { + (void)element; + print_json_indent(indent); + printf("JSON False\n"); +} + +void print_json_null(json_t *element, int indent) { + (void)element; + print_json_indent(indent); + printf("JSON Null\n"); +} + +/* + * Parse text into a JSON object. If text is valid JSON, returns a + * json_t structure, otherwise prints and error and returns null. + */ +json_t *load_json(const char *text) { + json_t *root; + json_error_t error; + + root = json_loads(text, 0, &error); + + if (root) { + return root; + } else { + fprintf(stderr, "json error on line %d: %s\n", error.line, error.text); + return (json_t *)0; + } +} + +/* + * Print a prompt and return (by reference) a null-terminated line of + * text. Returns NULL on eof or some error. + */ +char *read_line(char *line, int max_chars) { + printf("Type some JSON > "); + fflush(stdout); + return fgets(line, max_chars, stdin); +} + +/* ================================================================ + * main + */ + +#define MAX_CHARS 4096 + +int main(int argc, char *argv[]) { + char line[MAX_CHARS]; + + if (argc != 1) { + fprintf(stderr, "Usage: %s\n", argv[0]); + exit(-1); + } + + while (read_line(line, MAX_CHARS) != (char *)NULL) { + + /* parse text into JSON structure */ + json_t *root = load_json(line); + + if (root) { + /* print and release the JSON structure */ + print_json(root); + json_decref(root); + } + } + + return 0; +} diff --git a/lib/jansson/jansson.pc.in b/lib/jansson/jansson.pc.in new file mode 100644 index 0000000..69c9a43 --- /dev/null +++ b/lib/jansson/jansson.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Jansson +Description: Library for encoding, decoding and manipulating JSON data +Version: @VERSION@ +Libs: -L${libdir} -ljansson +Cflags: -I${includedir} diff --git a/lib/jansson/release.sh b/lib/jansson/release.sh new file mode 100755 index 0000000..c2551f8 --- /dev/null +++ b/lib/jansson/release.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Use this script to easily make releases of Jansson. It configures +# the source tree, and builds and signs all tarballs. + +die() { + echo $1 >&2 + exit 1 +} + +confirm() { + local answer + read -p "$1 [yN]: " answer + [ "$answer" = "Y" -o "$answer" = "y" ] || exit 0 +} + +set -e +[ -f configure.ac ] || die "Must be run at project root directory" + +# Determine version +v=$(grep AC_INIT configure.ac | sed -r 's/.*, \[(.+?)\],.*/\1/') +[ -n "$v" ] || die "Unable to determine version" +confirm "Version is $v, proceed?" + +# Sanity checks +vi=$(grep version-info src/Makefile.am | sed 's/^[ \t]*//g' | cut -d" " -f2) +confirm "Libtool version-info is $vi, proceed?" + +r=$(grep 'Released ' CHANGES | head -n 1) +confirm "Last CHANGES entry says \"$r\", proceed??" + +dv=$(grep ^version doc/conf.py | sed -r "s/.*'(.*)'.*/\1/") +if [ "$dv" != "$v" ]; then + die "Documentation version ($dv) doesn't match library version" +fi + +[ -f Makefile ] && make distclean || true +rm -f jansson-$v.tar.* +rm -rf jansson-$v-doc +rm -f jansson-$v-doc.tar.* + +autoreconf -fi +./configure + +# Run tests and make gz source tarball +: ${VALGRIND:=1} +export VALGRIND +make distcheck + +# Make bzip2 source tarball +make dist-bzip2 + +# Sign source tarballs +for s in gz bz2; do + gpg --detach-sign --armor jansson-$v.tar.$s +done + +# Build documentation +make html +mv doc/_build/html jansson-$v-doc + +# Make and sign documentation tarballs +for s in gz bz2; do + [ $s = gz ] && compress=gzip + [ $s = bz2 ] && compress=bzip2 + tar cf - jansson-$v-doc | $compress -9 -c > jansson-$v-doc.tar.$s + gpg --detach-sign --armor jansson-$v-doc.tar.$s +done + +echo "All done" diff --git a/lib/jansson/scripts/clang-format b/lib/jansson/scripts/clang-format new file mode 100755 index 0000000..65cb7a6 --- /dev/null +++ b/lib/jansson/scripts/clang-format @@ -0,0 +1,3 @@ +#!/bin/bash + +git ls-files | grep '\.[ch]$' | xargs clang-format -i diff --git a/lib/jansson/scripts/clang-format-check b/lib/jansson/scripts/clang-format-check new file mode 100755 index 0000000..a75e6b7 --- /dev/null +++ b/lib/jansson/scripts/clang-format-check @@ -0,0 +1,30 @@ +#!/bin/bash + +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} +CLANG_FORMAT_VERSION=${CLANG_FORMAT_VERSION:-} + +if ! type $CLANG_FORMAT >/dev/null || \ + ! $CLANG_FORMAT --version | grep -q "version ${CLANG_FORMAT_VERSION}"; then + # If running tests, mark this test as skipped. + exit 77 +fi + +errors=0 +paths=$(git ls-files | grep '\.[ch]$') +for path in $paths; do + echo "Checking $path" + $CLANG_FORMAT $path > $path.formatted + in=$(cat $path) + out=$(cat $path.formatted) + + if [ "$in" != "$out" ]; then + diff -u $path $path.formatted + errors=1 + fi + rm $path.formatted +done + +if [ $errors -ne 0 ]; then + echo "Formatting errors detected, run ./scripts/clang-format to fix!" + exit 1 +fi diff --git a/lib/jansson/src/Makefile.am b/lib/jansson/src/Makefile.am new file mode 100644 index 0000000..39cf34f --- /dev/null +++ b/lib/jansson/src/Makefile.am @@ -0,0 +1,35 @@ +EXTRA_DIST = jansson.def dtoa.c + +include_HEADERS = jansson.h +nodist_include_HEADERS = jansson_config.h + +lib_LTLIBRARIES = libjansson.la +libjansson_la_SOURCES = \ + dump.c \ + error.c \ + hashtable.c \ + hashtable.h \ + hashtable_seed.c \ + jansson_private.h \ + load.c \ + lookup3.h \ + memory.c \ + pack_unpack.c \ + strbuffer.c \ + strbuffer.h \ + strconv.c \ + utf.c \ + utf.h \ + value.c \ + version.c + +if DTOA_ENABLED +libjansson_la_SOURCES += dtoa.c +endif + +libjansson_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex '^json_|^jansson_' \ + -version-info 18:0:14 \ + @JSON_SYMVER_LDFLAGS@ \ + @JSON_BSYMBOLIC_LDFLAGS@ diff --git a/lib/jansson/src/dtoa.c b/lib/jansson/src/dtoa.c new file mode 100644 index 0000000..062ac24 --- /dev/null +++ b/lib/jansson/src/dtoa.c @@ -0,0 +1,6265 @@ +// clang-format off +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * (Note that IEEE arithmetic is disabled by gcc's -ffast-math flag.) + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +#include "jansson_private.h" + +#ifdef WORDS_BIGENDIAN +# define IEEE_MC68k 1 +#else +# define IEEE_8087 1 +#endif + +#if defined(JSON_INTEGER_IS_LONG_LONG) && (JSON_INTEGER_IS_LONG_LONG == 0) +# define NO_LONG_LONG 1 +#endif + +#define MALLOC jsonp_malloc +#define FREE jsonp_free + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. This will cause dtoa modes 4 and 5 to be + * treated the same as modes 2 and 3 for some inputs. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS + * is also #defined, fegetround() will be queried for the rounding mode. + * Note that both FLT_ROUNDS and fegetround() are specified by the C99 + * standard (and are specified to be consistent, with fesetround() + * affecting the value of FLT_ROUNDS), but that some (Linux) systems + * do not work correctly in this regard, so using fegetround() is more + * portable than using FLT_ROUNDS directly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic + * that rounds toward +Infinity. + * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased + * rounding when the underlying floating-point arithmetic uses + * unbiased rounding. This prevent using ordinary floating-point + * arithmetic when the result could be computed with one rounding error. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). On some systems + * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + + * When MULTIPLE_THREADS is #defined, this source file provides + * void set_max_dtoa_threads(unsigned int n); + * and expects + * unsigned int dtoa_get_threadno(void); + * to be available (possibly provided by + * #define dtoa_get_threadno omp_get_thread_num + * if OpenMP is in use or by + * #define dtoa_get_threadno pthread_self + * if Pthreads is in use), to return the current thread number. + * If set_max_dtoa_threads(n) was called and the current thread + * number is k with k < n, then calls on ACQUIRE_DTOA_LOCK(...) and + * FREE_DTOA_LOCK(...) are avoided; instead each thread with thread + * number < n has a separate copy of relevant data structures. + * After set_max_dtoa_threads(n), a call set_max_dtoa_threads(m) + * with m <= n has has no effect, but a call with m > n is honored. + * Such a call invokes REALLOC (assumed to be "realloc" if REALLOC + * is not #defined) to extend the size of the relevant array. + + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + * When errno should be assigned, under seemingly rare conditions + * it may be necessary to define Set_errno(x) suitably, e.g., in + * a local errno.h, such as + * #include + * #define Set_errno(x) _set_errno(x) + * #define NO_HEX_FP to omit recognition of hexadecimal floating-point + * values by strtod. + * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now) + * to disable logic for "fast" testing of very long input strings + * to strtod. This testing proceeds by initially truncating the + * input string, then if necessary comparing the whole string with + * a decimal expansion to decide close cases. This logic is only + * used for input more than STRTOD_DIGLIM digits long (default 40). + */ + +#ifndef Long +#define Long int +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#define Debug(x) x +int dtoa_stats[7]; /* strtod_{64,96,bigcomp},dtoa_{exact,64,96,bigcomp} */ +#else +#define assert(x) /*nothing*/ +#define Debug(x) /*nothing*/ +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef Honor_FLT_ROUNDS +#ifndef Trust_FLT_ROUNDS +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef MALLOC +extern void *MALLOC(size_t); +#else +#define MALLOC malloc +#endif + +#ifdef REALLOC +extern void *REALLOC(void*,size_t); +#else +#define REALLOC realloc +#endif + +#ifndef FREE +#define FREE free +#endif + +#ifdef __cplusplus + } +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#else +#undef INFNAN_CHECK +#define NO_STRTOD_BIGCOMP +#endif + +#include "errno.h" + +#ifdef NO_ERRNO /*{*/ +#undef Set_errno +#define Set_errno(x) +#else +#ifndef Set_errno +#define Set_errno(x) errno = x +#endif +#endif /*}*/ + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +#undef USE_BF96 + +#ifdef NO_LONG_LONG /*{{*/ +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /*}{ long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#ifndef NO_BF96 /*{*/ +#define USE_BF96 + +#ifdef SET_INEXACT +#define dtoa_divmax 27 +#else +int dtoa_divmax = 2; /* Permit experimenting: on some systems, 64-bit integer */ + /* division is slow enough that we may sometimes want to */ + /* avoid using it. We assume (but do not check) that */ + /* dtoa_divmax <= 27.*/ +#endif + +typedef struct BF96 { /* Normalized 96-bit software floating point numbers */ + unsigned int b0,b1,b2; /* b0 = most significant, binary point just to its left */ + int e; /* number represented = b * 2^e, with .5 <= b < 1 */ + } BF96; + + static BF96 pten[667] = { + { 0xeef453d6, 0x923bd65a, 0x113faa29, -1136 }, + { 0x9558b466, 0x1b6565f8, 0x4ac7ca59, -1132 }, + { 0xbaaee17f, 0xa23ebf76, 0x5d79bcf0, -1129 }, + { 0xe95a99df, 0x8ace6f53, 0xf4d82c2c, -1126 }, + { 0x91d8a02b, 0xb6c10594, 0x79071b9b, -1122 }, + { 0xb64ec836, 0xa47146f9, 0x9748e282, -1119 }, + { 0xe3e27a44, 0x4d8d98b7, 0xfd1b1b23, -1116 }, + { 0x8e6d8c6a, 0xb0787f72, 0xfe30f0f5, -1112 }, + { 0xb208ef85, 0x5c969f4f, 0xbdbd2d33, -1109 }, + { 0xde8b2b66, 0xb3bc4723, 0xad2c7880, -1106 }, + { 0x8b16fb20, 0x3055ac76, 0x4c3bcb50, -1102 }, + { 0xaddcb9e8, 0x3c6b1793, 0xdf4abe24, -1099 }, + { 0xd953e862, 0x4b85dd78, 0xd71d6dad, -1096 }, + { 0x87d4713d, 0x6f33aa6b, 0x8672648c, -1092 }, + { 0xa9c98d8c, 0xcb009506, 0x680efdaf, -1089 }, + { 0xd43bf0ef, 0xfdc0ba48, 0x0212bd1b, -1086 }, + { 0x84a57695, 0xfe98746d, 0x014bb630, -1082 }, + { 0xa5ced43b, 0x7e3e9188, 0x419ea3bd, -1079 }, + { 0xcf42894a, 0x5dce35ea, 0x52064cac, -1076 }, + { 0x818995ce, 0x7aa0e1b2, 0x7343efeb, -1072 }, + { 0xa1ebfb42, 0x19491a1f, 0x1014ebe6, -1069 }, + { 0xca66fa12, 0x9f9b60a6, 0xd41a26e0, -1066 }, + { 0xfd00b897, 0x478238d0, 0x8920b098, -1063 }, + { 0x9e20735e, 0x8cb16382, 0x55b46e5f, -1059 }, + { 0xc5a89036, 0x2fddbc62, 0xeb2189f7, -1056 }, + { 0xf712b443, 0xbbd52b7b, 0xa5e9ec75, -1053 }, + { 0x9a6bb0aa, 0x55653b2d, 0x47b233c9, -1049 }, + { 0xc1069cd4, 0xeabe89f8, 0x999ec0bb, -1046 }, + { 0xf148440a, 0x256e2c76, 0xc00670ea, -1043 }, + { 0x96cd2a86, 0x5764dbca, 0x38040692, -1039 }, + { 0xbc807527, 0xed3e12bc, 0xc6050837, -1036 }, + { 0xeba09271, 0xe88d976b, 0xf7864a44, -1033 }, + { 0x93445b87, 0x31587ea3, 0x7ab3ee6a, -1029 }, + { 0xb8157268, 0xfdae9e4c, 0x5960ea05, -1026 }, + { 0xe61acf03, 0x3d1a45df, 0x6fb92487, -1023 }, + { 0x8fd0c162, 0x06306bab, 0xa5d3b6d4, -1019 }, + { 0xb3c4f1ba, 0x87bc8696, 0x8f48a489, -1016 }, + { 0xe0b62e29, 0x29aba83c, 0x331acdab, -1013 }, + { 0x8c71dcd9, 0xba0b4925, 0x9ff0c08b, -1009 }, + { 0xaf8e5410, 0x288e1b6f, 0x07ecf0ae, -1006 }, + { 0xdb71e914, 0x32b1a24a, 0xc9e82cd9, -1003 }, + { 0x892731ac, 0x9faf056e, 0xbe311c08, -999 }, + { 0xab70fe17, 0xc79ac6ca, 0x6dbd630a, -996 }, + { 0xd64d3d9d, 0xb981787d, 0x092cbbcc, -993 }, + { 0x85f04682, 0x93f0eb4e, 0x25bbf560, -989 }, + { 0xa76c5823, 0x38ed2621, 0xaf2af2b8, -986 }, + { 0xd1476e2c, 0x07286faa, 0x1af5af66, -983 }, + { 0x82cca4db, 0x847945ca, 0x50d98d9f, -979 }, + { 0xa37fce12, 0x6597973c, 0xe50ff107, -976 }, + { 0xcc5fc196, 0xfefd7d0c, 0x1e53ed49, -973 }, + { 0xff77b1fc, 0xbebcdc4f, 0x25e8e89c, -970 }, + { 0x9faacf3d, 0xf73609b1, 0x77b19161, -966 }, + { 0xc795830d, 0x75038c1d, 0xd59df5b9, -963 }, + { 0xf97ae3d0, 0xd2446f25, 0x4b057328, -960 }, + { 0x9becce62, 0x836ac577, 0x4ee367f9, -956 }, + { 0xc2e801fb, 0x244576d5, 0x229c41f7, -953 }, + { 0xf3a20279, 0xed56d48a, 0x6b435275, -950 }, + { 0x9845418c, 0x345644d6, 0x830a1389, -946 }, + { 0xbe5691ef, 0x416bd60c, 0x23cc986b, -943 }, + { 0xedec366b, 0x11c6cb8f, 0x2cbfbe86, -940 }, + { 0x94b3a202, 0xeb1c3f39, 0x7bf7d714, -936 }, + { 0xb9e08a83, 0xa5e34f07, 0xdaf5ccd9, -933 }, + { 0xe858ad24, 0x8f5c22c9, 0xd1b3400f, -930 }, + { 0x91376c36, 0xd99995be, 0x23100809, -926 }, + { 0xb5854744, 0x8ffffb2d, 0xabd40a0c, -923 }, + { 0xe2e69915, 0xb3fff9f9, 0x16c90c8f, -920 }, + { 0x8dd01fad, 0x907ffc3b, 0xae3da7d9, -916 }, + { 0xb1442798, 0xf49ffb4a, 0x99cd11cf, -913 }, + { 0xdd95317f, 0x31c7fa1d, 0x40405643, -910 }, + { 0x8a7d3eef, 0x7f1cfc52, 0x482835ea, -906 }, + { 0xad1c8eab, 0x5ee43b66, 0xda324365, -903 }, + { 0xd863b256, 0x369d4a40, 0x90bed43e, -900 }, + { 0x873e4f75, 0xe2224e68, 0x5a7744a6, -896 }, + { 0xa90de353, 0x5aaae202, 0x711515d0, -893 }, + { 0xd3515c28, 0x31559a83, 0x0d5a5b44, -890 }, + { 0x8412d999, 0x1ed58091, 0xe858790a, -886 }, + { 0xa5178fff, 0x668ae0b6, 0x626e974d, -883 }, + { 0xce5d73ff, 0x402d98e3, 0xfb0a3d21, -880 }, + { 0x80fa687f, 0x881c7f8e, 0x7ce66634, -876 }, + { 0xa139029f, 0x6a239f72, 0x1c1fffc1, -873 }, + { 0xc9874347, 0x44ac874e, 0xa327ffb2, -870 }, + { 0xfbe91419, 0x15d7a922, 0x4bf1ff9f, -867 }, + { 0x9d71ac8f, 0xada6c9b5, 0x6f773fc3, -863 }, + { 0xc4ce17b3, 0x99107c22, 0xcb550fb4, -860 }, + { 0xf6019da0, 0x7f549b2b, 0x7e2a53a1, -857 }, + { 0x99c10284, 0x4f94e0fb, 0x2eda7444, -853 }, + { 0xc0314325, 0x637a1939, 0xfa911155, -850 }, + { 0xf03d93ee, 0xbc589f88, 0x793555ab, -847 }, + { 0x96267c75, 0x35b763b5, 0x4bc1558b, -843 }, + { 0xbbb01b92, 0x83253ca2, 0x9eb1aaed, -840 }, + { 0xea9c2277, 0x23ee8bcb, 0x465e15a9, -837 }, + { 0x92a1958a, 0x7675175f, 0x0bfacd89, -833 }, + { 0xb749faed, 0x14125d36, 0xcef980ec, -830 }, + { 0xe51c79a8, 0x5916f484, 0x82b7e127, -827 }, + { 0x8f31cc09, 0x37ae58d2, 0xd1b2ecb8, -823 }, + { 0xb2fe3f0b, 0x8599ef07, 0x861fa7e6, -820 }, + { 0xdfbdcece, 0x67006ac9, 0x67a791e0, -817 }, + { 0x8bd6a141, 0x006042bd, 0xe0c8bb2c, -813 }, + { 0xaecc4991, 0x4078536d, 0x58fae9f7, -810 }, + { 0xda7f5bf5, 0x90966848, 0xaf39a475, -807 }, + { 0x888f9979, 0x7a5e012d, 0x6d8406c9, -803 }, + { 0xaab37fd7, 0xd8f58178, 0xc8e5087b, -800 }, + { 0xd5605fcd, 0xcf32e1d6, 0xfb1e4a9a, -797 }, + { 0x855c3be0, 0xa17fcd26, 0x5cf2eea0, -793 }, + { 0xa6b34ad8, 0xc9dfc06f, 0xf42faa48, -790 }, + { 0xd0601d8e, 0xfc57b08b, 0xf13b94da, -787 }, + { 0x823c1279, 0x5db6ce57, 0x76c53d08, -783 }, + { 0xa2cb1717, 0xb52481ed, 0x54768c4b, -780 }, + { 0xcb7ddcdd, 0xa26da268, 0xa9942f5d, -777 }, + { 0xfe5d5415, 0x0b090b02, 0xd3f93b35, -774 }, + { 0x9efa548d, 0x26e5a6e1, 0xc47bc501, -770 }, + { 0xc6b8e9b0, 0x709f109a, 0x359ab641, -767 }, + { 0xf867241c, 0x8cc6d4c0, 0xc30163d2, -764 }, + { 0x9b407691, 0xd7fc44f8, 0x79e0de63, -760 }, + { 0xc2109436, 0x4dfb5636, 0x985915fc, -757 }, + { 0xf294b943, 0xe17a2bc4, 0x3e6f5b7b, -754 }, + { 0x979cf3ca, 0x6cec5b5a, 0xa705992c, -750 }, + { 0xbd8430bd, 0x08277231, 0x50c6ff78, -747 }, + { 0xece53cec, 0x4a314ebd, 0xa4f8bf56, -744 }, + { 0x940f4613, 0xae5ed136, 0x871b7795, -740 }, + { 0xb9131798, 0x99f68584, 0x28e2557b, -737 }, + { 0xe757dd7e, 0xc07426e5, 0x331aeada, -734 }, + { 0x9096ea6f, 0x3848984f, 0x3ff0d2c8, -730 }, + { 0xb4bca50b, 0x065abe63, 0x0fed077a, -727 }, + { 0xe1ebce4d, 0xc7f16dfb, 0xd3e84959, -724 }, + { 0x8d3360f0, 0x9cf6e4bd, 0x64712dd7, -720 }, + { 0xb080392c, 0xc4349dec, 0xbd8d794d, -717 }, + { 0xdca04777, 0xf541c567, 0xecf0d7a0, -714 }, + { 0x89e42caa, 0xf9491b60, 0xf41686c4, -710 }, + { 0xac5d37d5, 0xb79b6239, 0x311c2875, -707 }, + { 0xd77485cb, 0x25823ac7, 0x7d633293, -704 }, + { 0x86a8d39e, 0xf77164bc, 0xae5dff9c, -700 }, + { 0xa8530886, 0xb54dbdeb, 0xd9f57f83, -697 }, + { 0xd267caa8, 0x62a12d66, 0xd072df63, -694 }, + { 0x8380dea9, 0x3da4bc60, 0x4247cb9e, -690 }, + { 0xa4611653, 0x8d0deb78, 0x52d9be85, -687 }, + { 0xcd795be8, 0x70516656, 0x67902e27, -684 }, + { 0x806bd971, 0x4632dff6, 0x00ba1cd8, -680 }, + { 0xa086cfcd, 0x97bf97f3, 0x80e8a40e, -677 }, + { 0xc8a883c0, 0xfdaf7df0, 0x6122cd12, -674 }, + { 0xfad2a4b1, 0x3d1b5d6c, 0x796b8057, -671 }, + { 0x9cc3a6ee, 0xc6311a63, 0xcbe33036, -667 }, + { 0xc3f490aa, 0x77bd60fc, 0xbedbfc44, -664 }, + { 0xf4f1b4d5, 0x15acb93b, 0xee92fb55, -661 }, + { 0x99171105, 0x2d8bf3c5, 0x751bdd15, -657 }, + { 0xbf5cd546, 0x78eef0b6, 0xd262d45a, -654 }, + { 0xef340a98, 0x172aace4, 0x86fb8971, -651 }, + { 0x9580869f, 0x0e7aac0e, 0xd45d35e6, -647 }, + { 0xbae0a846, 0xd2195712, 0x89748360, -644 }, + { 0xe998d258, 0x869facd7, 0x2bd1a438, -641 }, + { 0x91ff8377, 0x5423cc06, 0x7b6306a3, -637 }, + { 0xb67f6455, 0x292cbf08, 0x1a3bc84c, -634 }, + { 0xe41f3d6a, 0x7377eeca, 0x20caba5f, -631 }, + { 0x8e938662, 0x882af53e, 0x547eb47b, -627 }, + { 0xb23867fb, 0x2a35b28d, 0xe99e619a, -624 }, + { 0xdec681f9, 0xf4c31f31, 0x6405fa00, -621 }, + { 0x8b3c113c, 0x38f9f37e, 0xde83bc40, -617 }, + { 0xae0b158b, 0x4738705e, 0x9624ab50, -614 }, + { 0xd98ddaee, 0x19068c76, 0x3badd624, -611 }, + { 0x87f8a8d4, 0xcfa417c9, 0xe54ca5d7, -607 }, + { 0xa9f6d30a, 0x038d1dbc, 0x5e9fcf4c, -604 }, + { 0xd47487cc, 0x8470652b, 0x7647c320, -601 }, + { 0x84c8d4df, 0xd2c63f3b, 0x29ecd9f4, -597 }, + { 0xa5fb0a17, 0xc777cf09, 0xf4681071, -594 }, + { 0xcf79cc9d, 0xb955c2cc, 0x7182148d, -591 }, + { 0x81ac1fe2, 0x93d599bf, 0xc6f14cd8, -587 }, + { 0xa21727db, 0x38cb002f, 0xb8ada00e, -584 }, + { 0xca9cf1d2, 0x06fdc03b, 0xa6d90811, -581 }, + { 0xfd442e46, 0x88bd304a, 0x908f4a16, -578 }, + { 0x9e4a9cec, 0x15763e2e, 0x9a598e4e, -574 }, + { 0xc5dd4427, 0x1ad3cdba, 0x40eff1e1, -571 }, + { 0xf7549530, 0xe188c128, 0xd12bee59, -568 }, + { 0x9a94dd3e, 0x8cf578b9, 0x82bb74f8, -564 }, + { 0xc13a148e, 0x3032d6e7, 0xe36a5236, -561 }, + { 0xf18899b1, 0xbc3f8ca1, 0xdc44e6c3, -558 }, + { 0x96f5600f, 0x15a7b7e5, 0x29ab103a, -554 }, + { 0xbcb2b812, 0xdb11a5de, 0x7415d448, -551 }, + { 0xebdf6617, 0x91d60f56, 0x111b495b, -548 }, + { 0x936b9fce, 0xbb25c995, 0xcab10dd9, -544 }, + { 0xb84687c2, 0x69ef3bfb, 0x3d5d514f, -541 }, + { 0xe65829b3, 0x046b0afa, 0x0cb4a5a3, -538 }, + { 0x8ff71a0f, 0xe2c2e6dc, 0x47f0e785, -534 }, + { 0xb3f4e093, 0xdb73a093, 0x59ed2167, -531 }, + { 0xe0f218b8, 0xd25088b8, 0x306869c1, -528 }, + { 0x8c974f73, 0x83725573, 0x1e414218, -524 }, + { 0xafbd2350, 0x644eeacf, 0xe5d1929e, -521 }, + { 0xdbac6c24, 0x7d62a583, 0xdf45f746, -518 }, + { 0x894bc396, 0xce5da772, 0x6b8bba8c, -514 }, + { 0xab9eb47c, 0x81f5114f, 0x066ea92f, -511 }, + { 0xd686619b, 0xa27255a2, 0xc80a537b, -508 }, + { 0x8613fd01, 0x45877585, 0xbd06742c, -504 }, + { 0xa798fc41, 0x96e952e7, 0x2c481138, -501 }, + { 0xd17f3b51, 0xfca3a7a0, 0xf75a1586, -498 }, + { 0x82ef8513, 0x3de648c4, 0x9a984d73, -494 }, + { 0xa3ab6658, 0x0d5fdaf5, 0xc13e60d0, -491 }, + { 0xcc963fee, 0x10b7d1b3, 0x318df905, -488 }, + { 0xffbbcfe9, 0x94e5c61f, 0xfdf17746, -485 }, + { 0x9fd561f1, 0xfd0f9bd3, 0xfeb6ea8b, -481 }, + { 0xc7caba6e, 0x7c5382c8, 0xfe64a52e, -478 }, + { 0xf9bd690a, 0x1b68637b, 0x3dfdce7a, -475 }, + { 0x9c1661a6, 0x51213e2d, 0x06bea10c, -471 }, + { 0xc31bfa0f, 0xe5698db8, 0x486e494f, -468 }, + { 0xf3e2f893, 0xdec3f126, 0x5a89dba3, -465 }, + { 0x986ddb5c, 0x6b3a76b7, 0xf8962946, -461 }, + { 0xbe895233, 0x86091465, 0xf6bbb397, -458 }, + { 0xee2ba6c0, 0x678b597f, 0x746aa07d, -455 }, + { 0x94db4838, 0x40b717ef, 0xa8c2a44e, -451 }, + { 0xba121a46, 0x50e4ddeb, 0x92f34d62, -448 }, + { 0xe896a0d7, 0xe51e1566, 0x77b020ba, -445 }, + { 0x915e2486, 0xef32cd60, 0x0ace1474, -441 }, + { 0xb5b5ada8, 0xaaff80b8, 0x0d819992, -438 }, + { 0xe3231912, 0xd5bf60e6, 0x10e1fff6, -435 }, + { 0x8df5efab, 0xc5979c8f, 0xca8d3ffa, -431 }, + { 0xb1736b96, 0xb6fd83b3, 0xbd308ff8, -428 }, + { 0xddd0467c, 0x64bce4a0, 0xac7cb3f6, -425 }, + { 0x8aa22c0d, 0xbef60ee4, 0x6bcdf07a, -421 }, + { 0xad4ab711, 0x2eb3929d, 0x86c16c98, -418 }, + { 0xd89d64d5, 0x7a607744, 0xe871c7bf, -415 }, + { 0x87625f05, 0x6c7c4a8b, 0x11471cd7, -411 }, + { 0xa93af6c6, 0xc79b5d2d, 0xd598e40d, -408 }, + { 0xd389b478, 0x79823479, 0x4aff1d10, -405 }, + { 0x843610cb, 0x4bf160cb, 0xcedf722a, -401 }, + { 0xa54394fe, 0x1eedb8fe, 0xc2974eb4, -398 }, + { 0xce947a3d, 0xa6a9273e, 0x733d2262, -395 }, + { 0x811ccc66, 0x8829b887, 0x0806357d, -391 }, + { 0xa163ff80, 0x2a3426a8, 0xca07c2dc, -388 }, + { 0xc9bcff60, 0x34c13052, 0xfc89b393, -385 }, + { 0xfc2c3f38, 0x41f17c67, 0xbbac2078, -382 }, + { 0x9d9ba783, 0x2936edc0, 0xd54b944b, -378 }, + { 0xc5029163, 0xf384a931, 0x0a9e795e, -375 }, + { 0xf64335bc, 0xf065d37d, 0x4d4617b5, -372 }, + { 0x99ea0196, 0x163fa42e, 0x504bced1, -368 }, + { 0xc06481fb, 0x9bcf8d39, 0xe45ec286, -365 }, + { 0xf07da27a, 0x82c37088, 0x5d767327, -362 }, + { 0x964e858c, 0x91ba2655, 0x3a6a07f8, -358 }, + { 0xbbe226ef, 0xb628afea, 0x890489f7, -355 }, + { 0xeadab0ab, 0xa3b2dbe5, 0x2b45ac74, -352 }, + { 0x92c8ae6b, 0x464fc96f, 0x3b0b8bc9, -348 }, + { 0xb77ada06, 0x17e3bbcb, 0x09ce6ebb, -345 }, + { 0xe5599087, 0x9ddcaabd, 0xcc420a6a, -342 }, + { 0x8f57fa54, 0xc2a9eab6, 0x9fa94682, -338 }, + { 0xb32df8e9, 0xf3546564, 0x47939822, -335 }, + { 0xdff97724, 0x70297ebd, 0x59787e2b, -332 }, + { 0x8bfbea76, 0xc619ef36, 0x57eb4edb, -328 }, + { 0xaefae514, 0x77a06b03, 0xede62292, -325 }, + { 0xdab99e59, 0x958885c4, 0xe95fab36, -322 }, + { 0x88b402f7, 0xfd75539b, 0x11dbcb02, -318 }, + { 0xaae103b5, 0xfcd2a881, 0xd652bdc2, -315 }, + { 0xd59944a3, 0x7c0752a2, 0x4be76d33, -312 }, + { 0x857fcae6, 0x2d8493a5, 0x6f70a440, -308 }, + { 0xa6dfbd9f, 0xb8e5b88e, 0xcb4ccd50, -305 }, + { 0xd097ad07, 0xa71f26b2, 0x7e2000a4, -302 }, + { 0x825ecc24, 0xc873782f, 0x8ed40066, -298 }, + { 0xa2f67f2d, 0xfa90563b, 0x72890080, -295 }, + { 0xcbb41ef9, 0x79346bca, 0x4f2b40a0, -292 }, + { 0xfea126b7, 0xd78186bc, 0xe2f610c8, -289 }, + { 0x9f24b832, 0xe6b0f436, 0x0dd9ca7d, -285 }, + { 0xc6ede63f, 0xa05d3143, 0x91503d1c, -282 }, + { 0xf8a95fcf, 0x88747d94, 0x75a44c63, -279 }, + { 0x9b69dbe1, 0xb548ce7c, 0xc986afbe, -275 }, + { 0xc24452da, 0x229b021b, 0xfbe85bad, -272 }, + { 0xf2d56790, 0xab41c2a2, 0xfae27299, -269 }, + { 0x97c560ba, 0x6b0919a5, 0xdccd879f, -265 }, + { 0xbdb6b8e9, 0x05cb600f, 0x5400e987, -262 }, + { 0xed246723, 0x473e3813, 0x290123e9, -259 }, + { 0x9436c076, 0x0c86e30b, 0xf9a0b672, -255 }, + { 0xb9447093, 0x8fa89bce, 0xf808e40e, -252 }, + { 0xe7958cb8, 0x7392c2c2, 0xb60b1d12, -249 }, + { 0x90bd77f3, 0x483bb9b9, 0xb1c6f22b, -245 }, + { 0xb4ecd5f0, 0x1a4aa828, 0x1e38aeb6, -242 }, + { 0xe2280b6c, 0x20dd5232, 0x25c6da63, -239 }, + { 0x8d590723, 0x948a535f, 0x579c487e, -235 }, + { 0xb0af48ec, 0x79ace837, 0x2d835a9d, -232 }, + { 0xdcdb1b27, 0x98182244, 0xf8e43145, -229 }, + { 0x8a08f0f8, 0xbf0f156b, 0x1b8e9ecb, -225 }, + { 0xac8b2d36, 0xeed2dac5, 0xe272467e, -222 }, + { 0xd7adf884, 0xaa879177, 0x5b0ed81d, -219 }, + { 0x86ccbb52, 0xea94baea, 0x98e94712, -215 }, + { 0xa87fea27, 0xa539e9a5, 0x3f2398d7, -212 }, + { 0xd29fe4b1, 0x8e88640e, 0x8eec7f0d, -209 }, + { 0x83a3eeee, 0xf9153e89, 0x1953cf68, -205 }, + { 0xa48ceaaa, 0xb75a8e2b, 0x5fa8c342, -202 }, + { 0xcdb02555, 0x653131b6, 0x3792f412, -199 }, + { 0x808e1755, 0x5f3ebf11, 0xe2bbd88b, -195 }, + { 0xa0b19d2a, 0xb70e6ed6, 0x5b6aceae, -192 }, + { 0xc8de0475, 0x64d20a8b, 0xf245825a, -189 }, + { 0xfb158592, 0xbe068d2e, 0xeed6e2f0, -186 }, + { 0x9ced737b, 0xb6c4183d, 0x55464dd6, -182 }, + { 0xc428d05a, 0xa4751e4c, 0xaa97e14c, -179 }, + { 0xf5330471, 0x4d9265df, 0xd53dd99f, -176 }, + { 0x993fe2c6, 0xd07b7fab, 0xe546a803, -172 }, + { 0xbf8fdb78, 0x849a5f96, 0xde985204, -169 }, + { 0xef73d256, 0xa5c0f77c, 0x963e6685, -166 }, + { 0x95a86376, 0x27989aad, 0xdde70013, -162 }, + { 0xbb127c53, 0xb17ec159, 0x5560c018, -159 }, + { 0xe9d71b68, 0x9dde71af, 0xaab8f01e, -156 }, + { 0x92267121, 0x62ab070d, 0xcab39613, -152 }, + { 0xb6b00d69, 0xbb55c8d1, 0x3d607b97, -149 }, + { 0xe45c10c4, 0x2a2b3b05, 0x8cb89a7d, -146 }, + { 0x8eb98a7a, 0x9a5b04e3, 0x77f3608e, -142 }, + { 0xb267ed19, 0x40f1c61c, 0x55f038b2, -139 }, + { 0xdf01e85f, 0x912e37a3, 0x6b6c46de, -136 }, + { 0x8b61313b, 0xbabce2c6, 0x2323ac4b, -132 }, + { 0xae397d8a, 0xa96c1b77, 0xabec975e, -129 }, + { 0xd9c7dced, 0x53c72255, 0x96e7bd35, -126 }, + { 0x881cea14, 0x545c7575, 0x7e50d641, -122 }, + { 0xaa242499, 0x697392d2, 0xdde50bd1, -119 }, + { 0xd4ad2dbf, 0xc3d07787, 0x955e4ec6, -116 }, + { 0x84ec3c97, 0xda624ab4, 0xbd5af13b, -112 }, + { 0xa6274bbd, 0xd0fadd61, 0xecb1ad8a, -109 }, + { 0xcfb11ead, 0x453994ba, 0x67de18ed, -106 }, + { 0x81ceb32c, 0x4b43fcf4, 0x80eacf94, -102 }, + { 0xa2425ff7, 0x5e14fc31, 0xa1258379, -99 }, + { 0xcad2f7f5, 0x359a3b3e, 0x096ee458, -96 }, + { 0xfd87b5f2, 0x8300ca0d, 0x8bca9d6e, -93 }, + { 0x9e74d1b7, 0x91e07e48, 0x775ea264, -89 }, + { 0xc6120625, 0x76589dda, 0x95364afe, -86 }, + { 0xf79687ae, 0xd3eec551, 0x3a83ddbd, -83 }, + { 0x9abe14cd, 0x44753b52, 0xc4926a96, -79 }, + { 0xc16d9a00, 0x95928a27, 0x75b7053c, -76 }, + { 0xf1c90080, 0xbaf72cb1, 0x5324c68b, -73 }, + { 0x971da050, 0x74da7bee, 0xd3f6fc16, -69 }, + { 0xbce50864, 0x92111aea, 0x88f4bb1c, -66 }, + { 0xec1e4a7d, 0xb69561a5, 0x2b31e9e3, -63 }, + { 0x9392ee8e, 0x921d5d07, 0x3aff322e, -59 }, + { 0xb877aa32, 0x36a4b449, 0x09befeb9, -56 }, + { 0xe69594be, 0xc44de15b, 0x4c2ebe68, -53 }, + { 0x901d7cf7, 0x3ab0acd9, 0x0f9d3701, -49 }, + { 0xb424dc35, 0x095cd80f, 0x538484c1, -46 }, + { 0xe12e1342, 0x4bb40e13, 0x2865a5f2, -43 }, + { 0x8cbccc09, 0x6f5088cb, 0xf93f87b7, -39 }, + { 0xafebff0b, 0xcb24aafe, 0xf78f69a5, -36 }, + { 0xdbe6fece, 0xbdedd5be, 0xb573440e, -33 }, + { 0x89705f41, 0x36b4a597, 0x31680a88, -29 }, + { 0xabcc7711, 0x8461cefc, 0xfdc20d2b, -26 }, + { 0xd6bf94d5, 0xe57a42bc, 0x3d329076, -23 }, + { 0x8637bd05, 0xaf6c69b5, 0xa63f9a49, -19 }, + { 0xa7c5ac47, 0x1b478423, 0x0fcf80dc, -16 }, + { 0xd1b71758, 0xe219652b, 0xd3c36113, -13 }, + { 0x83126e97, 0x8d4fdf3b, 0x645a1cac, -9 }, + { 0xa3d70a3d, 0x70a3d70a, 0x3d70a3d7, -6 }, + { 0xcccccccc, 0xcccccccc, 0xcccccccc, -3 }, + { 0x80000000, 0x00000000, 0x00000000, 1 }, + { 0xa0000000, 0x00000000, 0x00000000, 4 }, + { 0xc8000000, 0x00000000, 0x00000000, 7 }, + { 0xfa000000, 0x00000000, 0x00000000, 10 }, + { 0x9c400000, 0x00000000, 0x00000000, 14 }, + { 0xc3500000, 0x00000000, 0x00000000, 17 }, + { 0xf4240000, 0x00000000, 0x00000000, 20 }, + { 0x98968000, 0x00000000, 0x00000000, 24 }, + { 0xbebc2000, 0x00000000, 0x00000000, 27 }, + { 0xee6b2800, 0x00000000, 0x00000000, 30 }, + { 0x9502f900, 0x00000000, 0x00000000, 34 }, + { 0xba43b740, 0x00000000, 0x00000000, 37 }, + { 0xe8d4a510, 0x00000000, 0x00000000, 40 }, + { 0x9184e72a, 0x00000000, 0x00000000, 44 }, + { 0xb5e620f4, 0x80000000, 0x00000000, 47 }, + { 0xe35fa931, 0xa0000000, 0x00000000, 50 }, + { 0x8e1bc9bf, 0x04000000, 0x00000000, 54 }, + { 0xb1a2bc2e, 0xc5000000, 0x00000000, 57 }, + { 0xde0b6b3a, 0x76400000, 0x00000000, 60 }, + { 0x8ac72304, 0x89e80000, 0x00000000, 64 }, + { 0xad78ebc5, 0xac620000, 0x00000000, 67 }, + { 0xd8d726b7, 0x177a8000, 0x00000000, 70 }, + { 0x87867832, 0x6eac9000, 0x00000000, 74 }, + { 0xa968163f, 0x0a57b400, 0x00000000, 77 }, + { 0xd3c21bce, 0xcceda100, 0x00000000, 80 }, + { 0x84595161, 0x401484a0, 0x00000000, 84 }, + { 0xa56fa5b9, 0x9019a5c8, 0x00000000, 87 }, + { 0xcecb8f27, 0xf4200f3a, 0x00000000, 90 }, + { 0x813f3978, 0xf8940984, 0x40000000, 94 }, + { 0xa18f07d7, 0x36b90be5, 0x50000000, 97 }, + { 0xc9f2c9cd, 0x04674ede, 0xa4000000, 100 }, + { 0xfc6f7c40, 0x45812296, 0x4d000000, 103 }, + { 0x9dc5ada8, 0x2b70b59d, 0xf0200000, 107 }, + { 0xc5371912, 0x364ce305, 0x6c280000, 110 }, + { 0xf684df56, 0xc3e01bc6, 0xc7320000, 113 }, + { 0x9a130b96, 0x3a6c115c, 0x3c7f4000, 117 }, + { 0xc097ce7b, 0xc90715b3, 0x4b9f1000, 120 }, + { 0xf0bdc21a, 0xbb48db20, 0x1e86d400, 123 }, + { 0x96769950, 0xb50d88f4, 0x13144480, 127 }, + { 0xbc143fa4, 0xe250eb31, 0x17d955a0, 130 }, + { 0xeb194f8e, 0x1ae525fd, 0x5dcfab08, 133 }, + { 0x92efd1b8, 0xd0cf37be, 0x5aa1cae5, 137 }, + { 0xb7abc627, 0x050305ad, 0xf14a3d9e, 140 }, + { 0xe596b7b0, 0xc643c719, 0x6d9ccd05, 143 }, + { 0x8f7e32ce, 0x7bea5c6f, 0xe4820023, 147 }, + { 0xb35dbf82, 0x1ae4f38b, 0xdda2802c, 150 }, + { 0xe0352f62, 0xa19e306e, 0xd50b2037, 153 }, + { 0x8c213d9d, 0xa502de45, 0x4526f422, 157 }, + { 0xaf298d05, 0x0e4395d6, 0x9670b12b, 160 }, + { 0xdaf3f046, 0x51d47b4c, 0x3c0cdd76, 163 }, + { 0x88d8762b, 0xf324cd0f, 0xa5880a69, 167 }, + { 0xab0e93b6, 0xefee0053, 0x8eea0d04, 170 }, + { 0xd5d238a4, 0xabe98068, 0x72a49045, 173 }, + { 0x85a36366, 0xeb71f041, 0x47a6da2b, 177 }, + { 0xa70c3c40, 0xa64e6c51, 0x999090b6, 180 }, + { 0xd0cf4b50, 0xcfe20765, 0xfff4b4e3, 183 }, + { 0x82818f12, 0x81ed449f, 0xbff8f10e, 187 }, + { 0xa321f2d7, 0x226895c7, 0xaff72d52, 190 }, + { 0xcbea6f8c, 0xeb02bb39, 0x9bf4f8a6, 193 }, + { 0xfee50b70, 0x25c36a08, 0x02f236d0, 196 }, + { 0x9f4f2726, 0x179a2245, 0x01d76242, 200 }, + { 0xc722f0ef, 0x9d80aad6, 0x424d3ad2, 203 }, + { 0xf8ebad2b, 0x84e0d58b, 0xd2e08987, 206 }, + { 0x9b934c3b, 0x330c8577, 0x63cc55f4, 210 }, + { 0xc2781f49, 0xffcfa6d5, 0x3cbf6b71, 213 }, + { 0xf316271c, 0x7fc3908a, 0x8bef464e, 216 }, + { 0x97edd871, 0xcfda3a56, 0x97758bf0, 220 }, + { 0xbde94e8e, 0x43d0c8ec, 0x3d52eeed, 223 }, + { 0xed63a231, 0xd4c4fb27, 0x4ca7aaa8, 226 }, + { 0x945e455f, 0x24fb1cf8, 0x8fe8caa9, 230 }, + { 0xb975d6b6, 0xee39e436, 0xb3e2fd53, 233 }, + { 0xe7d34c64, 0xa9c85d44, 0x60dbbca8, 236 }, + { 0x90e40fbe, 0xea1d3a4a, 0xbc8955e9, 240 }, + { 0xb51d13ae, 0xa4a488dd, 0x6babab63, 243 }, + { 0xe264589a, 0x4dcdab14, 0xc696963c, 246 }, + { 0x8d7eb760, 0x70a08aec, 0xfc1e1de5, 250 }, + { 0xb0de6538, 0x8cc8ada8, 0x3b25a55f, 253 }, + { 0xdd15fe86, 0xaffad912, 0x49ef0eb7, 256 }, + { 0x8a2dbf14, 0x2dfcc7ab, 0x6e356932, 260 }, + { 0xacb92ed9, 0x397bf996, 0x49c2c37f, 263 }, + { 0xd7e77a8f, 0x87daf7fb, 0xdc33745e, 266 }, + { 0x86f0ac99, 0xb4e8dafd, 0x69a028bb, 270 }, + { 0xa8acd7c0, 0x222311bc, 0xc40832ea, 273 }, + { 0xd2d80db0, 0x2aabd62b, 0xf50a3fa4, 276 }, + { 0x83c7088e, 0x1aab65db, 0x792667c6, 280 }, + { 0xa4b8cab1, 0xa1563f52, 0x577001b8, 283 }, + { 0xcde6fd5e, 0x09abcf26, 0xed4c0226, 286 }, + { 0x80b05e5a, 0xc60b6178, 0x544f8158, 290 }, + { 0xa0dc75f1, 0x778e39d6, 0x696361ae, 293 }, + { 0xc913936d, 0xd571c84c, 0x03bc3a19, 296 }, + { 0xfb587849, 0x4ace3a5f, 0x04ab48a0, 299 }, + { 0x9d174b2d, 0xcec0e47b, 0x62eb0d64, 303 }, + { 0xc45d1df9, 0x42711d9a, 0x3ba5d0bd, 306 }, + { 0xf5746577, 0x930d6500, 0xca8f44ec, 309 }, + { 0x9968bf6a, 0xbbe85f20, 0x7e998b13, 313 }, + { 0xbfc2ef45, 0x6ae276e8, 0x9e3fedd8, 316 }, + { 0xefb3ab16, 0xc59b14a2, 0xc5cfe94e, 319 }, + { 0x95d04aee, 0x3b80ece5, 0xbba1f1d1, 323 }, + { 0xbb445da9, 0xca61281f, 0x2a8a6e45, 326 }, + { 0xea157514, 0x3cf97226, 0xf52d09d7, 329 }, + { 0x924d692c, 0xa61be758, 0x593c2626, 333 }, + { 0xb6e0c377, 0xcfa2e12e, 0x6f8b2fb0, 336 }, + { 0xe498f455, 0xc38b997a, 0x0b6dfb9c, 339 }, + { 0x8edf98b5, 0x9a373fec, 0x4724bd41, 343 }, + { 0xb2977ee3, 0x00c50fe7, 0x58edec91, 346 }, + { 0xdf3d5e9b, 0xc0f653e1, 0x2f2967b6, 349 }, + { 0x8b865b21, 0x5899f46c, 0xbd79e0d2, 353 }, + { 0xae67f1e9, 0xaec07187, 0xecd85906, 356 }, + { 0xda01ee64, 0x1a708de9, 0xe80e6f48, 359 }, + { 0x884134fe, 0x908658b2, 0x3109058d, 363 }, + { 0xaa51823e, 0x34a7eede, 0xbd4b46f0, 366 }, + { 0xd4e5e2cd, 0xc1d1ea96, 0x6c9e18ac, 369 }, + { 0x850fadc0, 0x9923329e, 0x03e2cf6b, 373 }, + { 0xa6539930, 0xbf6bff45, 0x84db8346, 376 }, + { 0xcfe87f7c, 0xef46ff16, 0xe6126418, 379 }, + { 0x81f14fae, 0x158c5f6e, 0x4fcb7e8f, 383 }, + { 0xa26da399, 0x9aef7749, 0xe3be5e33, 386 }, + { 0xcb090c80, 0x01ab551c, 0x5cadf5bf, 389 }, + { 0xfdcb4fa0, 0x02162a63, 0x73d9732f, 392 }, + { 0x9e9f11c4, 0x014dda7e, 0x2867e7fd, 396 }, + { 0xc646d635, 0x01a1511d, 0xb281e1fd, 399 }, + { 0xf7d88bc2, 0x4209a565, 0x1f225a7c, 402 }, + { 0x9ae75759, 0x6946075f, 0x3375788d, 406 }, + { 0xc1a12d2f, 0xc3978937, 0x0052d6b1, 409 }, + { 0xf209787b, 0xb47d6b84, 0xc0678c5d, 412 }, + { 0x9745eb4d, 0x50ce6332, 0xf840b7ba, 416 }, + { 0xbd176620, 0xa501fbff, 0xb650e5a9, 419 }, + { 0xec5d3fa8, 0xce427aff, 0xa3e51f13, 422 }, + { 0x93ba47c9, 0x80e98cdf, 0xc66f336c, 426 }, + { 0xb8a8d9bb, 0xe123f017, 0xb80b0047, 429 }, + { 0xe6d3102a, 0xd96cec1d, 0xa60dc059, 432 }, + { 0x9043ea1a, 0xc7e41392, 0x87c89837, 436 }, + { 0xb454e4a1, 0x79dd1877, 0x29babe45, 439 }, + { 0xe16a1dc9, 0xd8545e94, 0xf4296dd6, 442 }, + { 0x8ce2529e, 0x2734bb1d, 0x1899e4a6, 446 }, + { 0xb01ae745, 0xb101e9e4, 0x5ec05dcf, 449 }, + { 0xdc21a117, 0x1d42645d, 0x76707543, 452 }, + { 0x899504ae, 0x72497eba, 0x6a06494a, 456 }, + { 0xabfa45da, 0x0edbde69, 0x0487db9d, 459 }, + { 0xd6f8d750, 0x9292d603, 0x45a9d284, 462 }, + { 0x865b8692, 0x5b9bc5c2, 0x0b8a2392, 466 }, + { 0xa7f26836, 0xf282b732, 0x8e6cac77, 469 }, + { 0xd1ef0244, 0xaf2364ff, 0x3207d795, 472 }, + { 0x8335616a, 0xed761f1f, 0x7f44e6bd, 476 }, + { 0xa402b9c5, 0xa8d3a6e7, 0x5f16206c, 479 }, + { 0xcd036837, 0x130890a1, 0x36dba887, 482 }, + { 0x80222122, 0x6be55a64, 0xc2494954, 486 }, + { 0xa02aa96b, 0x06deb0fd, 0xf2db9baa, 489 }, + { 0xc83553c5, 0xc8965d3d, 0x6f928294, 492 }, + { 0xfa42a8b7, 0x3abbf48c, 0xcb772339, 495 }, + { 0x9c69a972, 0x84b578d7, 0xff2a7604, 499 }, + { 0xc38413cf, 0x25e2d70d, 0xfef51385, 502 }, + { 0xf46518c2, 0xef5b8cd1, 0x7eb25866, 505 }, + { 0x98bf2f79, 0xd5993802, 0xef2f773f, 509 }, + { 0xbeeefb58, 0x4aff8603, 0xaafb550f, 512 }, + { 0xeeaaba2e, 0x5dbf6784, 0x95ba2a53, 515 }, + { 0x952ab45c, 0xfa97a0b2, 0xdd945a74, 519 }, + { 0xba756174, 0x393d88df, 0x94f97111, 522 }, + { 0xe912b9d1, 0x478ceb17, 0x7a37cd56, 525 }, + { 0x91abb422, 0xccb812ee, 0xac62e055, 529 }, + { 0xb616a12b, 0x7fe617aa, 0x577b986b, 532 }, + { 0xe39c4976, 0x5fdf9d94, 0xed5a7e85, 535 }, + { 0x8e41ade9, 0xfbebc27d, 0x14588f13, 539 }, + { 0xb1d21964, 0x7ae6b31c, 0x596eb2d8, 542 }, + { 0xde469fbd, 0x99a05fe3, 0x6fca5f8e, 545 }, + { 0x8aec23d6, 0x80043bee, 0x25de7bb9, 549 }, + { 0xada72ccc, 0x20054ae9, 0xaf561aa7, 552 }, + { 0xd910f7ff, 0x28069da4, 0x1b2ba151, 555 }, + { 0x87aa9aff, 0x79042286, 0x90fb44d2, 559 }, + { 0xa99541bf, 0x57452b28, 0x353a1607, 562 }, + { 0xd3fa922f, 0x2d1675f2, 0x42889b89, 565 }, + { 0x847c9b5d, 0x7c2e09b7, 0x69956135, 569 }, + { 0xa59bc234, 0xdb398c25, 0x43fab983, 572 }, + { 0xcf02b2c2, 0x1207ef2e, 0x94f967e4, 575 }, + { 0x8161afb9, 0x4b44f57d, 0x1d1be0ee, 579 }, + { 0xa1ba1ba7, 0x9e1632dc, 0x6462d92a, 582 }, + { 0xca28a291, 0x859bbf93, 0x7d7b8f75, 585 }, + { 0xfcb2cb35, 0xe702af78, 0x5cda7352, 588 }, + { 0x9defbf01, 0xb061adab, 0x3a088813, 592 }, + { 0xc56baec2, 0x1c7a1916, 0x088aaa18, 595 }, + { 0xf6c69a72, 0xa3989f5b, 0x8aad549e, 598 }, + { 0x9a3c2087, 0xa63f6399, 0x36ac54e2, 602 }, + { 0xc0cb28a9, 0x8fcf3c7f, 0x84576a1b, 605 }, + { 0xf0fdf2d3, 0xf3c30b9f, 0x656d44a2, 608 }, + { 0x969eb7c4, 0x7859e743, 0x9f644ae5, 612 }, + { 0xbc4665b5, 0x96706114, 0x873d5d9f, 615 }, + { 0xeb57ff22, 0xfc0c7959, 0xa90cb506, 618 }, + { 0x9316ff75, 0xdd87cbd8, 0x09a7f124, 622 }, + { 0xb7dcbf53, 0x54e9bece, 0x0c11ed6d, 625 }, + { 0xe5d3ef28, 0x2a242e81, 0x8f1668c8, 628 }, + { 0x8fa47579, 0x1a569d10, 0xf96e017d, 632 }, + { 0xb38d92d7, 0x60ec4455, 0x37c981dc, 635 }, + { 0xe070f78d, 0x3927556a, 0x85bbe253, 638 }, + { 0x8c469ab8, 0x43b89562, 0x93956d74, 642 }, + { 0xaf584166, 0x54a6babb, 0x387ac8d1, 645 }, + { 0xdb2e51bf, 0xe9d0696a, 0x06997b05, 648 }, + { 0x88fcf317, 0xf22241e2, 0x441fece3, 652 }, + { 0xab3c2fdd, 0xeeaad25a, 0xd527e81c, 655 }, + { 0xd60b3bd5, 0x6a5586f1, 0x8a71e223, 658 }, + { 0x85c70565, 0x62757456, 0xf6872d56, 662 }, + { 0xa738c6be, 0xbb12d16c, 0xb428f8ac, 665 }, + { 0xd106f86e, 0x69d785c7, 0xe13336d7, 668 }, + { 0x82a45b45, 0x0226b39c, 0xecc00246, 672 }, + { 0xa34d7216, 0x42b06084, 0x27f002d7, 675 }, + { 0xcc20ce9b, 0xd35c78a5, 0x31ec038d, 678 }, + { 0xff290242, 0xc83396ce, 0x7e670471, 681 }, + { 0x9f79a169, 0xbd203e41, 0x0f0062c6, 685 }, + { 0xc75809c4, 0x2c684dd1, 0x52c07b78, 688 }, + { 0xf92e0c35, 0x37826145, 0xa7709a56, 691 }, + { 0x9bbcc7a1, 0x42b17ccb, 0x88a66076, 695 }, + { 0xc2abf989, 0x935ddbfe, 0x6acff893, 698 }, + { 0xf356f7eb, 0xf83552fe, 0x0583f6b8, 701 }, + { 0x98165af3, 0x7b2153de, 0xc3727a33, 705 }, + { 0xbe1bf1b0, 0x59e9a8d6, 0x744f18c0, 708 }, + { 0xeda2ee1c, 0x7064130c, 0x1162def0, 711 }, + { 0x9485d4d1, 0xc63e8be7, 0x8addcb56, 715 }, + { 0xb9a74a06, 0x37ce2ee1, 0x6d953e2b, 718 }, + { 0xe8111c87, 0xc5c1ba99, 0xc8fa8db6, 721 }, + { 0x910ab1d4, 0xdb9914a0, 0x1d9c9892, 725 }, + { 0xb54d5e4a, 0x127f59c8, 0x2503beb6, 728 }, + { 0xe2a0b5dc, 0x971f303a, 0x2e44ae64, 731 }, + { 0x8da471a9, 0xde737e24, 0x5ceaecfe, 735 }, + { 0xb10d8e14, 0x56105dad, 0x7425a83e, 738 }, + { 0xdd50f199, 0x6b947518, 0xd12f124e, 741 }, + { 0x8a5296ff, 0xe33cc92f, 0x82bd6b70, 745 }, + { 0xace73cbf, 0xdc0bfb7b, 0x636cc64d, 748 }, + { 0xd8210bef, 0xd30efa5a, 0x3c47f7e0, 751 }, + { 0x8714a775, 0xe3e95c78, 0x65acfaec, 755 }, + { 0xa8d9d153, 0x5ce3b396, 0x7f1839a7, 758 }, + { 0xd31045a8, 0x341ca07c, 0x1ede4811, 761 }, + { 0x83ea2b89, 0x2091e44d, 0x934aed0a, 765 }, + { 0xa4e4b66b, 0x68b65d60, 0xf81da84d, 768 }, + { 0xce1de406, 0x42e3f4b9, 0x36251260, 771 }, + { 0x80d2ae83, 0xe9ce78f3, 0xc1d72b7c, 775 }, + { 0xa1075a24, 0xe4421730, 0xb24cf65b, 778 }, + { 0xc94930ae, 0x1d529cfc, 0xdee033f2, 781 }, + { 0xfb9b7cd9, 0xa4a7443c, 0x169840ef, 784 }, + { 0x9d412e08, 0x06e88aa5, 0x8e1f2895, 788 }, + { 0xc491798a, 0x08a2ad4e, 0xf1a6f2ba, 791 }, + { 0xf5b5d7ec, 0x8acb58a2, 0xae10af69, 794 }, + { 0x9991a6f3, 0xd6bf1765, 0xacca6da1, 798 }, + { 0xbff610b0, 0xcc6edd3f, 0x17fd090a, 801 }, + { 0xeff394dc, 0xff8a948e, 0xddfc4b4c, 804 }, + { 0x95f83d0a, 0x1fb69cd9, 0x4abdaf10, 808 }, + { 0xbb764c4c, 0xa7a4440f, 0x9d6d1ad4, 811 }, + { 0xea53df5f, 0xd18d5513, 0x84c86189, 814 }, + { 0x92746b9b, 0xe2f8552c, 0x32fd3cf5, 818 }, + { 0xb7118682, 0xdbb66a77, 0x3fbc8c33, 821 }, + { 0xe4d5e823, 0x92a40515, 0x0fabaf3f, 824 }, + { 0x8f05b116, 0x3ba6832d, 0x29cb4d87, 828 }, + { 0xb2c71d5b, 0xca9023f8, 0x743e20e9, 831 }, + { 0xdf78e4b2, 0xbd342cf6, 0x914da924, 834 }, + { 0x8bab8eef, 0xb6409c1a, 0x1ad089b6, 838 }, + { 0xae9672ab, 0xa3d0c320, 0xa184ac24, 841 }, + { 0xda3c0f56, 0x8cc4f3e8, 0xc9e5d72d, 844 }, + { 0x88658996, 0x17fb1871, 0x7e2fa67c, 848 }, + { 0xaa7eebfb, 0x9df9de8d, 0xddbb901b, 851 }, + { 0xd51ea6fa, 0x85785631, 0x552a7422, 854 }, + { 0x8533285c, 0x936b35de, 0xd53a8895, 858 }, + { 0xa67ff273, 0xb8460356, 0x8a892aba, 861 }, + { 0xd01fef10, 0xa657842c, 0x2d2b7569, 864 }, + { 0x8213f56a, 0x67f6b29b, 0x9c3b2962, 868 }, + { 0xa298f2c5, 0x01f45f42, 0x8349f3ba, 871 }, + { 0xcb3f2f76, 0x42717713, 0x241c70a9, 874 }, + { 0xfe0efb53, 0xd30dd4d7, 0xed238cd3, 877 }, + { 0x9ec95d14, 0x63e8a506, 0xf4363804, 881 }, + { 0xc67bb459, 0x7ce2ce48, 0xb143c605, 884 }, + { 0xf81aa16f, 0xdc1b81da, 0xdd94b786, 887 }, + { 0x9b10a4e5, 0xe9913128, 0xca7cf2b4, 891 }, + { 0xc1d4ce1f, 0x63f57d72, 0xfd1c2f61, 894 }, + { 0xf24a01a7, 0x3cf2dccf, 0xbc633b39, 897 }, + { 0x976e4108, 0x8617ca01, 0xd5be0503, 901 }, + { 0xbd49d14a, 0xa79dbc82, 0x4b2d8644, 904 }, + { 0xec9c459d, 0x51852ba2, 0xddf8e7d6, 907 }, + { 0x93e1ab82, 0x52f33b45, 0xcabb90e5, 911 }, + { 0xb8da1662, 0xe7b00a17, 0x3d6a751f, 914 }, + { 0xe7109bfb, 0xa19c0c9d, 0x0cc51267, 917 }, + { 0x906a617d, 0x450187e2, 0x27fb2b80, 921 }, + { 0xb484f9dc, 0x9641e9da, 0xb1f9f660, 924 }, + { 0xe1a63853, 0xbbd26451, 0x5e7873f8, 927 }, + { 0x8d07e334, 0x55637eb2, 0xdb0b487b, 931 }, + { 0xb049dc01, 0x6abc5e5f, 0x91ce1a9a, 934 }, + { 0xdc5c5301, 0xc56b75f7, 0x7641a140, 937 }, + { 0x89b9b3e1, 0x1b6329ba, 0xa9e904c8, 941 }, + { 0xac2820d9, 0x623bf429, 0x546345fa, 944 }, + { 0xd732290f, 0xbacaf133, 0xa97c1779, 947 }, + { 0x867f59a9, 0xd4bed6c0, 0x49ed8eab, 951 }, + { 0xa81f3014, 0x49ee8c70, 0x5c68f256, 954 }, + { 0xd226fc19, 0x5c6a2f8c, 0x73832eec, 957 }, + { 0x83585d8f, 0xd9c25db7, 0xc831fd53, 961 }, + { 0xa42e74f3, 0xd032f525, 0xba3e7ca8, 964 }, + { 0xcd3a1230, 0xc43fb26f, 0x28ce1bd2, 967 }, + { 0x80444b5e, 0x7aa7cf85, 0x7980d163, 971 }, + { 0xa0555e36, 0x1951c366, 0xd7e105bc, 974 }, + { 0xc86ab5c3, 0x9fa63440, 0x8dd9472b, 977 }, + { 0xfa856334, 0x878fc150, 0xb14f98f6, 980 }, + { 0x9c935e00, 0xd4b9d8d2, 0x6ed1bf9a, 984 }, + { 0xc3b83581, 0x09e84f07, 0x0a862f80, 987 }, + { 0xf4a642e1, 0x4c6262c8, 0xcd27bb61, 990 }, + { 0x98e7e9cc, 0xcfbd7dbd, 0x8038d51c, 994 }, + { 0xbf21e440, 0x03acdd2c, 0xe0470a63, 997 }, + { 0xeeea5d50, 0x04981478, 0x1858ccfc, 1000 }, + { 0x95527a52, 0x02df0ccb, 0x0f37801e, 1004 }, + { 0xbaa718e6, 0x8396cffd, 0xd3056025, 1007 }, + { 0xe950df20, 0x247c83fd, 0x47c6b82e, 1010 }, + { 0x91d28b74, 0x16cdd27e, 0x4cdc331d, 1014 }, + { 0xb6472e51, 0x1c81471d, 0xe0133fe4, 1017 }, + { 0xe3d8f9e5, 0x63a198e5, 0x58180fdd, 1020 }, + { 0x8e679c2f, 0x5e44ff8f, 0x570f09ea, 1024 }, + { 0xb201833b, 0x35d63f73, 0x2cd2cc65, 1027 }, + { 0xde81e40a, 0x034bcf4f, 0xf8077f7e, 1030 }, + { 0x8b112e86, 0x420f6191, 0xfb04afaf, 1034 }, + { 0xadd57a27, 0xd29339f6, 0x79c5db9a, 1037 }, + { 0xd94ad8b1, 0xc7380874, 0x18375281, 1040 }, + { 0x87cec76f, 0x1c830548, 0x8f229391, 1044 }, + { 0xa9c2794a, 0xe3a3c69a, 0xb2eb3875, 1047 }, + { 0xd433179d, 0x9c8cb841, 0x5fa60692, 1050 }, + { 0x849feec2, 0x81d7f328, 0xdbc7c41b, 1054 }, + { 0xa5c7ea73, 0x224deff3, 0x12b9b522, 1057 }, + { 0xcf39e50f, 0xeae16bef, 0xd768226b, 1060 }, + { 0x81842f29, 0xf2cce375, 0xe6a11583, 1064 }, + { 0xa1e53af4, 0x6f801c53, 0x60495ae3, 1067 }, + { 0xca5e89b1, 0x8b602368, 0x385bb19c, 1070 }, + { 0xfcf62c1d, 0xee382c42, 0x46729e03, 1073 }, + { 0x9e19db92, 0xb4e31ba9, 0x6c07a2c2, 1077 } + }; + static short int Lhint[2098] = { + /*18,*/19, 19, 19, 19, 20, 20, 20, 21, 21, + 21, 22, 22, 22, 23, 23, 23, 23, 24, 24, + 24, 25, 25, 25, 26, 26, 26, 26, 27, 27, + 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 31, 31, 31, 32, 32, 32, 32, 33, 33, + 33, 34, 34, 34, 35, 35, 35, 35, 36, 36, + 36, 37, 37, 37, 38, 38, 38, 38, 39, 39, + 39, 40, 40, 40, 41, 41, 41, 41, 42, 42, + 42, 43, 43, 43, 44, 44, 44, 44, 45, 45, + 45, 46, 46, 46, 47, 47, 47, 47, 48, 48, + 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, + 51, 52, 52, 52, 53, 53, 53, 54, 54, 54, + 54, 55, 55, 55, 56, 56, 56, 57, 57, 57, + 57, 58, 58, 58, 59, 59, 59, 60, 60, 60, + 60, 61, 61, 61, 62, 62, 62, 63, 63, 63, + 63, 64, 64, 64, 65, 65, 65, 66, 66, 66, + 66, 67, 67, 67, 68, 68, 68, 69, 69, 69, + 69, 70, 70, 70, 71, 71, 71, 72, 72, 72, + 72, 73, 73, 73, 74, 74, 74, 75, 75, 75, + 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, + 78, 79, 79, 79, 80, 80, 80, 81, 81, 81, + 82, 82, 82, 82, 83, 83, 83, 84, 84, 84, + 85, 85, 85, 85, 86, 86, 86, 87, 87, 87, + 88, 88, 88, 88, 89, 89, 89, 90, 90, 90, + 91, 91, 91, 91, 92, 92, 92, 93, 93, 93, + 94, 94, 94, 94, 95, 95, 95, 96, 96, 96, + 97, 97, 97, 97, 98, 98, 98, 99, 99, 99, + 100, 100, 100, 100, 101, 101, 101, 102, 102, 102, + 103, 103, 103, 103, 104, 104, 104, 105, 105, 105, + 106, 106, 106, 106, 107, 107, 107, 108, 108, 108, + 109, 109, 109, 110, 110, 110, 110, 111, 111, 111, + 112, 112, 112, 113, 113, 113, 113, 114, 114, 114, + 115, 115, 115, 116, 116, 116, 116, 117, 117, 117, + 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, + 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, + 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, + 127, 127, 127, 128, 128, 128, 128, 129, 129, 129, + 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, + 133, 133, 133, 134, 134, 134, 134, 135, 135, 135, + 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, + 139, 139, 139, 140, 140, 140, 141, 141, 141, 141, + 142, 142, 142, 143, 143, 143, 144, 144, 144, 144, + 145, 145, 145, 146, 146, 146, 147, 147, 147, 147, + 148, 148, 148, 149, 149, 149, 150, 150, 150, 150, + 151, 151, 151, 152, 152, 152, 153, 153, 153, 153, + 154, 154, 154, 155, 155, 155, 156, 156, 156, 156, + 157, 157, 157, 158, 158, 158, 159, 159, 159, 159, + 160, 160, 160, 161, 161, 161, 162, 162, 162, 162, + 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, + 166, 166, 166, 167, 167, 167, 168, 168, 168, 169, + 169, 169, 169, 170, 170, 170, 171, 171, 171, 172, + 172, 172, 172, 173, 173, 173, 174, 174, 174, 175, + 175, 175, 175, 176, 176, 176, 177, 177, 177, 178, + 178, 178, 178, 179, 179, 179, 180, 180, 180, 181, + 181, 181, 181, 182, 182, 182, 183, 183, 183, 184, + 184, 184, 184, 185, 185, 185, 186, 186, 186, 187, + 187, 187, 187, 188, 188, 188, 189, 189, 189, 190, + 190, 190, 190, 191, 191, 191, 192, 192, 192, 193, + 193, 193, 193, 194, 194, 194, 195, 195, 195, 196, + 196, 196, 197, 197, 197, 197, 198, 198, 198, 199, + 199, 199, 200, 200, 200, 200, 201, 201, 201, 202, + 202, 202, 203, 203, 203, 203, 204, 204, 204, 205, + 205, 205, 206, 206, 206, 206, 207, 207, 207, 208, + 208, 208, 209, 209, 209, 209, 210, 210, 210, 211, + 211, 211, 212, 212, 212, 212, 213, 213, 213, 214, + 214, 214, 215, 215, 215, 215, 216, 216, 216, 217, + 217, 217, 218, 218, 218, 218, 219, 219, 219, 220, + 220, 220, 221, 221, 221, 221, 222, 222, 222, 223, + 223, 223, 224, 224, 224, 224, 225, 225, 225, 226, + 226, 226, 227, 227, 227, 228, 228, 228, 228, 229, + 229, 229, 230, 230, 230, 231, 231, 231, 231, 232, + 232, 232, 233, 233, 233, 234, 234, 234, 234, 235, + 235, 235, 236, 236, 236, 237, 237, 237, 237, 238, + 238, 238, 239, 239, 239, 240, 240, 240, 240, 241, + 241, 241, 242, 242, 242, 243, 243, 243, 243, 244, + 244, 244, 245, 245, 245, 246, 246, 246, 246, 247, + 247, 247, 248, 248, 248, 249, 249, 249, 249, 250, + 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, + 253, 253, 254, 254, 254, 255, 255, 255, 256, 256, + 256, 256, 257, 257, 257, 258, 258, 258, 259, 259, + 259, 259, 260, 260, 260, 261, 261, 261, 262, 262, + 262, 262, 263, 263, 263, 264, 264, 264, 265, 265, + 265, 265, 266, 266, 266, 267, 267, 267, 268, 268, + 268, 268, 269, 269, 269, 270, 270, 270, 271, 271, + 271, 271, 272, 272, 272, 273, 273, 273, 274, 274, + 274, 274, 275, 275, 275, 276, 276, 276, 277, 277, + 277, 277, 278, 278, 278, 279, 279, 279, 280, 280, + 280, 280, 281, 281, 281, 282, 282, 282, 283, 283, + 283, 283, 284, 284, 284, 285, 285, 285, 286, 286, + 286, 287, 287, 287, 287, 288, 288, 288, 289, 289, + 289, 290, 290, 290, 290, 291, 291, 291, 292, 292, + 292, 293, 293, 293, 293, 294, 294, 294, 295, 295, + 295, 296, 296, 296, 296, 297, 297, 297, 298, 298, + 298, 299, 299, 299, 299, 300, 300, 300, 301, 301, + 301, 302, 302, 302, 302, 303, 303, 303, 304, 304, + 304, 305, 305, 305, 305, 306, 306, 306, 307, 307, + 307, 308, 308, 308, 308, 309, 309, 309, 310, 310, + 310, 311, 311, 311, 311, 312, 312, 312, 313, 313, + 313, 314, 314, 314, 315, 315, 315, 315, 316, 316, + 316, 317, 317, 317, 318, 318, 318, 318, 319, 319, + 319, 320, 320, 320, 321, 321, 321, 321, 322, 322, + 322, 323, 323, 323, 324, 324, 324, 324, 325, 325, + 325, 326, 326, 326, 327, 327, 327, 327, 328, 328, + 328, 329, 329, 329, 330, 330, 330, 330, 331, 331, + 331, 332, 332, 332, 333, 333, 333, 333, 334, 334, + 334, 335, 335, 335, 336, 336, 336, 336, 337, 337, + 337, 338, 338, 338, 339, 339, 339, 339, 340, 340, + 340, 341, 341, 341, 342, 342, 342, 342, 343, 343, + 343, 344, 344, 344, 345, 345, 345, 346, 346, 346, + 346, 347, 347, 347, 348, 348, 348, 349, 349, 349, + 349, 350, 350, 350, 351, 351, 351, 352, 352, 352, + 352, 353, 353, 353, 354, 354, 354, 355, 355, 355, + 355, 356, 356, 356, 357, 357, 357, 358, 358, 358, + 358, 359, 359, 359, 360, 360, 360, 361, 361, 361, + 361, 362, 362, 362, 363, 363, 363, 364, 364, 364, + 364, 365, 365, 365, 366, 366, 366, 367, 367, 367, + 367, 368, 368, 368, 369, 369, 369, 370, 370, 370, + 370, 371, 371, 371, 372, 372, 372, 373, 373, 373, + 374, 374, 374, 374, 375, 375, 375, 376, 376, 376, + 377, 377, 377, 377, 378, 378, 378, 379, 379, 379, + 380, 380, 380, 380, 381, 381, 381, 382, 382, 382, + 383, 383, 383, 383, 384, 384, 384, 385, 385, 385, + 386, 386, 386, 386, 387, 387, 387, 388, 388, 388, + 389, 389, 389, 389, 390, 390, 390, 391, 391, 391, + 392, 392, 392, 392, 393, 393, 393, 394, 394, 394, + 395, 395, 395, 395, 396, 396, 396, 397, 397, 397, + 398, 398, 398, 398, 399, 399, 399, 400, 400, 400, + 401, 401, 401, 402, 402, 402, 402, 403, 403, 403, + 404, 404, 404, 405, 405, 405, 405, 406, 406, 406, + 407, 407, 407, 408, 408, 408, 408, 409, 409, 409, + 410, 410, 410, 411, 411, 411, 411, 412, 412, 412, + 413, 413, 413, 414, 414, 414, 414, 415, 415, 415, + 416, 416, 416, 417, 417, 417, 417, 418, 418, 418, + 419, 419, 419, 420, 420, 420, 420, 421, 421, 421, + 422, 422, 422, 423, 423, 423, 423, 424, 424, 424, + 425, 425, 425, 426, 426, 426, 426, 427, 427, 427, + 428, 428, 428, 429, 429, 429, 429, 430, 430, 430, + 431, 431, 431, 432, 432, 432, 433, 433, 433, 433, + 434, 434, 434, 435, 435, 435, 436, 436, 436, 436, + 437, 437, 437, 438, 438, 438, 439, 439, 439, 439, + 440, 440, 440, 441, 441, 441, 442, 442, 442, 442, + 443, 443, 443, 444, 444, 444, 445, 445, 445, 445, + 446, 446, 446, 447, 447, 447, 448, 448, 448, 448, + 449, 449, 449, 450, 450, 450, 451, 451, 451, 451, + 452, 452, 452, 453, 453, 453, 454, 454, 454, 454, + 455, 455, 455, 456, 456, 456, 457, 457, 457, 457, + 458, 458, 458, 459, 459, 459, 460, 460, 460, 461, + 461, 461, 461, 462, 462, 462, 463, 463, 463, 464, + 464, 464, 464, 465, 465, 465, 466, 466, 466, 467, + 467, 467, 467, 468, 468, 468, 469, 469, 469, 470, + 470, 470, 470, 471, 471, 471, 472, 472, 472, 473, + 473, 473, 473, 474, 474, 474, 475, 475, 475, 476, + 476, 476, 476, 477, 477, 477, 478, 478, 478, 479, + 479, 479, 479, 480, 480, 480, 481, 481, 481, 482, + 482, 482, 482, 483, 483, 483, 484, 484, 484, 485, + 485, 485, 485, 486, 486, 486, 487, 487, 487, 488, + 488, 488, 488, 489, 489, 489, 490, 490, 490, 491, + 491, 491, 492, 492, 492, 492, 493, 493, 493, 494, + 494, 494, 495, 495, 495, 495, 496, 496, 496, 497, + 497, 497, 498, 498, 498, 498, 499, 499, 499, 500, + 500, 500, 501, 501, 501, 501, 502, 502, 502, 503, + 503, 503, 504, 504, 504, 504, 505, 505, 505, 506, + 506, 506, 507, 507, 507, 507, 508, 508, 508, 509, + 509, 509, 510, 510, 510, 510, 511, 511, 511, 512, + 512, 512, 513, 513, 513, 513, 514, 514, 514, 515, + 515, 515, 516, 516, 516, 516, 517, 517, 517, 518, + 518, 518, 519, 519, 519, 520, 520, 520, 520, 521, + 521, 521, 522, 522, 522, 523, 523, 523, 523, 524, + 524, 524, 525, 525, 525, 526, 526, 526, 526, 527, + 527, 527, 528, 528, 528, 529, 529, 529, 529, 530, + 530, 530, 531, 531, 531, 532, 532, 532, 532, 533, + 533, 533, 534, 534, 534, 535, 535, 535, 535, 536, + 536, 536, 537, 537, 537, 538, 538, 538, 538, 539, + 539, 539, 540, 540, 540, 541, 541, 541, 541, 542, + 542, 542, 543, 543, 543, 544, 544, 544, 544, 545, + 545, 545, 546, 546, 546, 547, 547, 547, 548, 548, + 548, 548, 549, 549, 549, 550, 550, 550, 551, 551, + 551, 551, 552, 552, 552, 553, 553, 553, 554, 554, + 554, 554, 555, 555, 555, 556, 556, 556, 557, 557, + 557, 557, 558, 558, 558, 559, 559, 559, 560, 560, + 560, 560, 561, 561, 561, 562, 562, 562, 563, 563, + 563, 563, 564, 564, 564, 565, 565, 565, 566, 566, + 566, 566, 567, 567, 567, 568, 568, 568, 569, 569, + 569, 569, 570, 570, 570, 571, 571, 571, 572, 572, + 572, 572, 573, 573, 573, 574, 574, 574, 575, 575, + 575, 575, 576, 576, 576, 577, 577, 577, 578, 578, + 578, 579, 579, 579, 579, 580, 580, 580, 581, 581, + 581, 582, 582, 582, 582, 583, 583, 583, 584, 584, + 584, 585, 585, 585, 585, 586, 586, 586, 587, 587, + 587, 588, 588, 588, 588, 589, 589, 589, 590, 590, + 590, 591, 591, 591, 591, 592, 592, 592, 593, 593, + 593, 594, 594, 594, 594, 595, 595, 595, 596, 596, + 596, 597, 597, 597, 597, 598, 598, 598, 599, 599, + 599, 600, 600, 600, 600, 601, 601, 601, 602, 602, + 602, 603, 603, 603, 603, 604, 604, 604, 605, 605, + 605, 606, 606, 606, 607, 607, 607, 607, 608, 608, + 608, 609, 609, 609, 610, 610, 610, 610, 611, 611, + 611, 612, 612, 612, 613, 613, 613, 613, 614, 614, + 614, 615, 615, 615, 616, 616, 616, 616, 617, 617, + 617, 618, 618, 618, 619, 619, 619, 619, 620, 620, + 620, 621, 621, 621, 622, 622, 622, 622, 623, 623, + 623, 624, 624, 624, 625, 625, 625, 625, 626, 626, + 626, 627, 627, 627, 628, 628, 628, 628, 629, 629, + 629, 630, 630, 630, 631, 631, 631, 631, 632, 632, + 632, 633, 633, 633, 634, 634, 634, 634, 635, 635, + 635, 636, 636, 636, 637, 637, 637, 638, 638, 638, + 638, 639, 639, 639, 640, 640, 640, 641, 641, 641, + 641, 642, 642, 642, 643, 643, 643, 644, 644, 644, + 644, 645, 645, 645, 646, 646, 646, 647, 647, 647, + 647, 648, 648, 648, 649, 649, 649, 650, 650 }; + static ULLong pfive[27] = { + 5ll, + 25ll, + 125ll, + 625ll, + 3125ll, + 15625ll, + 78125ll, + 390625ll, + 1953125ll, + 9765625ll, + 48828125ll, + 244140625ll, + 1220703125ll, + 6103515625ll, + 30517578125ll, + 152587890625ll, + 762939453125ll, + 3814697265625ll, + 19073486328125ll, + 95367431640625ll, + 476837158203125ll, + 2384185791015625ll, + 11920928955078125ll, + 59604644775390625ll, + 298023223876953125ll, + 1490116119384765625ll, + 7450580596923828125ll + }; + + static int pfivebits[25] = {3, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28, 31, + 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 56, 59}; +#endif /*}*/ +#endif /*}} NO_LONG_LONG */ + +typedef union { double d; ULong L[2]; +#ifdef USE_BF96 + ULLong LL; +#endif + } U; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d +#define LLval(x) (x)->LL + +#ifndef STRTOD_DIGLIM +#define STRTOD_DIGLIM 40 +#endif + +#ifdef DIGLIM_DEBUG +extern int strtod_diglim; +#else +#define strtod_diglim STRTOD_DIGLIM +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Nbits 53 +#define Bias 1023 +#define Emax 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Nbits 56 +#define Bias 65 +#define Emax 248 +#define Emin (-260) +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Nbits 56 +#define Bias 129 +#define Emax 126 +#define Emin (-129) +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#else +#ifdef ROUND_BIASED_without_Round_Up +#undef ROUND_BIASED +#define ROUND_BIASED +#endif +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +typedef struct BCinfo BCinfo; + struct +BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; }; + +#define FFFFFFFF 0xffffffffUL + +#ifdef MULTIPLE_THREADS +#define MTa , PTI +#define MTb , &TI +#define MTd , ThInfo **PTI +static unsigned int maxthreads = 0; +#else +#define MTa /*nothing*/ +#define MTb /*nothing*/ +#define MTd /*nothing*/ +#endif + +#define Kmax 7 + +#ifdef __cplusplus +extern "C" double strtod(const char *s00, char **se); +extern "C" char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +#endif + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; + }; + + typedef struct Bigint Bigint; + typedef struct +ThInfo { + Bigint *Freelist[Kmax+1]; + Bigint *P5s; + } ThInfo; + + static ThInfo TI0; + +#ifdef MULTIPLE_THREADS + static ThInfo *TI1; + static int TI0_used; + + void +set_max_dtoa_threads(unsigned int n) +{ + size_t L; + + if (n > maxthreads) { + L = n*sizeof(ThInfo); + if (TI1) { + TI1 = (ThInfo*)REALLOC(TI1, L); + memset(TI1 + maxthreads, 0, (n-maxthreads)*sizeof(ThInfo)); + } + else { + TI1 = (ThInfo*)MALLOC(L); + if (TI0_used) { + memcpy(TI1, &TI0, sizeof(ThInfo)); + if (n > 1) + memset(TI1 + 1, 0, L - sizeof(ThInfo)); + memset(&TI0, 0, sizeof(ThInfo)); + } + else + memset(TI1, 0, L); + } + maxthreads = n; + } + } + + static ThInfo* +get_TI(void) +{ + unsigned int thno = dtoa_get_threadno(); + if (thno < maxthreads) + return TI1 + thno; + if (thno == 0) + TI0_used = 1; + return &TI0; + } +#define freelist TI->Freelist +#define p5s TI->P5s +#else +#define freelist TI0.Freelist +#define p5s TI0.P5s +#endif + + static Bigint * +Balloc(int k MTd) +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif +#ifdef MULTIPLE_THREADS + ThInfo *TI; + + if (!(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(0); +#endif + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) + freelist[k] = rv->next; + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && (unsigned long)(pmem_next - private_mem + len) <= PRIVATE_mem +#ifdef MULTIPLE_THREADS + && TI == TI1 +#endif + ) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } +#ifdef MULTIPLE_THREADS + if (TI == &TI0) + FREE_DTOA_LOCK(0); +#endif + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree(Bigint *v MTd) +{ +#ifdef MULTIPLE_THREADS + ThInfo *TI; +#endif + if (v) { + if (v->k > Kmax) + FREE((void*)v); + else { +#ifdef MULTIPLE_THREADS + if (!(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(0); +#endif + v->next = freelist[v->k]; + freelist[v->k] = v; +#ifdef MULTIPLE_THREADS + if (TI == &TI0) + FREE_DTOA_LOCK(0); +#endif + } + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + + static Bigint * +multadd(Bigint *b, int m, int a MTd) /* multiply by m and add a */ +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1 MTa); + Bcopy(b1, b); + Bfree(b MTa); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b(const char *s, int nd0, int nd, ULong y9, int dplen MTd) +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k MTa); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1 MTa); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0' MTa); + while(++i < nd0); + s += dplen; + } + else + s += dplen + 9; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0' MTa); + return b; + } + + static int +hi0bits(ULong x) +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits(ULong *y) +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b(int i MTd) +{ + Bigint *b; + + b = Balloc(1 MTa); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult(Bigint *a, Bigint *b MTd) +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k MTa); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if ((y = *xb & 0xffff)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if ((y = *xb >> 16)) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint * +pow5mult(Bigint *b, int k MTd) +{ + Bigint *b1, *p5, *p51; +#ifdef MULTIPLE_THREADS + ThInfo *TI; +#endif + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) + b = multadd(b, p05[i-1], 0 MTa); + + if (!(k >>= 2)) + return b; +#ifdef MULTIPLE_THREADS + if (!(TI = *PTI)) + *PTI = TI = get_TI(); +#endif + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + if (!(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625 MTa); + p5->next = 0; + } + if (TI == &TI0) + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625 MTa); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5 MTa); + Bfree(b MTa); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + if (!TI && !(TI = *PTI)) + *PTI = TI = get_TI(); + if (TI == &TI0) + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5 MTa); + p51->next = 0; + } + if (TI == &TI0) + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift(Bigint *b, int k MTd) +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1 MTa); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b MTa); + return b1; + } + + static int +cmp(Bigint *a, Bigint *b) +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff(Bigint *a, Bigint *b MTd) +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0 MTa); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k MTa); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp(U *x) +{ + Long L; + U u; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(&u) = L; + word1(&u) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&u) = 0x80000 >> L; + word1(&u) = 0; + } + else { + word0(&u) = 0; + L -= Exp_shift; + word1(&u) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(&u); + } + + static double +b2d(Bigint *a, int *e) +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(&d) = d0 >> 16 | d0 << 16; + word1(&d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(&d); + } + + static Bigint * +d2b(U *d, int *e, int *bits MTd) +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1 MTa); +#else + b = Balloc(2 MTa); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio(Bigint *a, Bigint *b) +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(&da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&da) *= 1 << k; + } + else { + k = -k; + word0(&db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&db) *= 1 << k; + } +#else + if (k > 0) + word0(&da) += k*Exp_msk1; + else { + k = -k; + word0(&db) += k*Exp_msk1; + } +#endif + return dval(&da) / dval(&db); + } + + static const double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static const double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-256 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static const double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static const double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#undef Need_Hexdig +#ifdef INFNAN_CHECK +#ifndef No_Hex_NaN +#define Need_Hexdig +#endif +#endif + +#ifndef Need_Hexdig +#ifndef NO_HEX_FP +#define Need_Hexdig +#endif +#endif + +#ifdef Need_Hexdig /*{*/ +#if 0 +static unsigned char hexdig[256]; + + static void +htinit(unsigned char *h, unsigned char *s, int inc) +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) + h[j] = i + inc; + } + + static void +hexdig_init(void) /* Use of hexdig_init omitted 20121220 to avoid a */ + /* race condition when multiple threads are used. */ +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); + } +#else +static unsigned char hexdig[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0, + 0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; +#endif +#endif /* } Need_Hexdig */ + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + + static int +match(const char **sp, const char *t) +{ + int c, d; + const char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } + +#ifndef No_Hex_NaN + static void +hexnan(U *rvp, const char **sp) +{ + ULong c, x[2]; + const char *s; + int c1, havedig, udx0, xshift; + + /**** if (!hexdig['0']) hexdig_init(); ****/ + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(const unsigned char*)(s+1)) && c <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) + s += 2; + while((c = *(const unsigned char*)++s)) { + if ((c1 = hexdig[c])) + c = c1 & 0xf; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } +#ifdef GDTOA_NON_PEDANTIC_NANCHECK + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ +#else + else { + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); + break; + } +#endif + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(rvp) = Exp_mask | x[0]; + word1(rvp) = x[1]; + } + } +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#endif + +#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/ + static Bigint * +increment(Bigint *b MTd) +{ + ULong *x, *xe; + Bigint *b1; + + x = b->x; + xe = x + b->wds; + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1 MTa); + Bcopy(b1,b); + Bfree(b MTa); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; + } + +#endif /*}*/ + +#ifndef NO_HEX_FP /*{*/ + + static void +rshift(Bigint *b, int k) +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = 32 - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & 0xffffffff; + y = *x++ >> k; + } + if ((*x1 = y) !=0) + x1++; + } + else + while(x < xe) + *x1++ = *x++; + } + if ((b->wds = x1 - b->x) == 0) + b->x[0] = 0; + } + + static ULong +any_on(Bigint *b, int k) +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) + n = nwds; + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) + return 1; + } + x0 = x; + x += n; + while(x > x0) + if (*--x) + return 1; + return 0; + } + +enum { /* rounding values: same as FLT_ROUNDS */ + Round_zero = 0, + Round_near = 1, + Round_up = 2, + Round_down = 3 + }; + + void +gethex(const char **sp, U *rvp, int rounding, int sign MTd) +{ + Bigint *b; + char d; + const unsigned char *decpt, *s0, *s, *s1; + Long e, e1; + ULong L, lostbits, *x; + int big, denorm, esign, havedig, k, n, nb, nbits, nz, up, zret; +#ifdef IBM + int j; +#endif + enum { +#ifdef IEEE_Arith /*{{*/ + emax = 0x7fe - Bias - P + 1, + emin = Emin - P + 1 +#else /*}{*/ + emin = Emin - P, +#ifdef VAX + emax = 0x7ff - Bias - P + 1 +#endif +#ifdef IBM + emax = 0x7f - Bias - P +#endif +#endif /*}}*/ + }; +#ifdef IEEE_Arith + int check_denorm = 0; +#endif +#ifdef USE_LOCALE + int i; +#ifdef NO_LOCALE_CACHE + const unsigned char *decimalpoint = (unsigned char*) + localeconv()->decimal_point; +#else + const unsigned char *decimalpoint; + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char*)localeconv()->decimal_point; + if ((decimalpoint_cache = (unsigned char*) + MALLOC(strlen((const char*)s0) + 1))) { + strcpy((char*)decimalpoint_cache, (const char*)s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + /**** if (!hexdig['0']) hexdig_init(); ****/ + havedig = 0; + s0 = *(const unsigned char **)sp + 2; + while(s0[havedig] == '0') + havedig++; + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) + havedig++; + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s != '.') + goto pcheck; + decpt = ++s; +#endif + if (!hexdig[*s]) + goto pcheck; + while(*s == '0') + s++; + if (hexdig[*s]) + zret = 0; + havedig = 1; + s0 = s; + } + while(hexdig[*s]) + s++; +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) + s++; + }/*}*/ + if (decpt) + e = -(((Long)(s-decpt)) << 2); + pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* fall through */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) + big = 1; + e1 = 10*e1 + n - 0x10; + } + if (esign) + e1 = -e1; + e += e1; + } + *sp = (char*)s; + if (!havedig) + *sp = (char*)s0 - 1; + if (zret) + goto retz1; + if (big) { + if (esign) { +#ifdef IEEE_Arith + switch(rounding) { + case Round_up: + if (sign) + break; + goto ret_tiny; + case Round_down: + if (!sign) + break; + goto ret_tiny; + } +#endif + goto retz; +#ifdef IEEE_Arith + ret_tinyf: + Bfree(b MTa); + ret_tiny: + Set_errno(ERANGE); + word0(rvp) = 0; + word1(rvp) = 1; + return; +#endif /* IEEE_Arith */ + } + switch(rounding) { + case Round_near: + goto ovfl1; + case Round_up: + if (!sign) + goto ovfl1; + goto ret_big; + case Round_down: + if (sign) + goto ovfl1; + goto ret_big; + } + ret_big: + word0(rvp) = Big0; + word1(rvp) = Big1; + return; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) + k++; + b = Balloc(k MTa); + x = b->x; + havedig = n = nz = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') + continue; +#endif + if ((d = hexdig[*s1])) + havedig = 1; + else if (!havedig) { + e += 4; + continue; + } + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (d & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + nb = ULbits*n - hi0bits(L); + nbits = Nbits; + lostbits = 0; + x = b->x; + if (nb > nbits) { + n = nb - nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) + lostbits = 3; + } + } + rshift(b, n); + e += n; + } + else if (nb < nbits) { + n = nbits - nb; + b = lshift(b, n MTa); + e -= n; + x = b->x; + } + if (e > emax) { + ovfl: + Bfree(b MTa); + ovfl1: + Set_errno(ERANGE); +#ifdef Honor_FLT_ROUNDS + switch (rounding) { + case Round_zero: + goto ret_big; + case Round_down: + if (!sign) + goto ret_big; + break; + case Round_up: + if (sign) + goto ret_big; + } +#endif + word0(rvp) = Exp_mask; + word1(rvp) = 0; + return; + } + denorm = 0; + if (e < emin) { + denorm = 1; + n = emin - e; + if (n >= nbits) { +#ifdef IEEE_Arith /*{*/ + switch (rounding) { + case Round_near: + if (n == nbits && (n < 2 || lostbits || any_on(b,n-1))) + goto ret_tinyf; + break; + case Round_up: + if (!sign) + goto ret_tinyf; + break; + case Round_down: + if (sign) + goto ret_tinyf; + } +#endif /* } IEEE_Arith */ + Bfree(b MTa); + retz: + Set_errno(ERANGE); + retz1: + rvp->d = 0.; + return; + } + k = n - 1; +#ifdef IEEE_Arith + if (!k) { + switch(rounding) { + case Round_near: + if (((b->x[0] & 3) == 3) || (lostbits && (b->x[0] & 1))) { + multadd(b, 1, 1 MTa); + emin_check: + if (b->x[1] == (1 << (Exp_shift + 1))) { + rshift(b,1); + e = emin; + goto normal; + } + } + break; + case Round_up: + if (!sign && (lostbits || (b->x[0] & 1))) { + incr_denorm: + multadd(b, 1, 2 MTa); + check_denorm = 1; + lostbits = 0; + goto emin_check; + } + break; + case Round_down: + if (sign && (lostbits || (b->x[0] & 1))) + goto incr_denorm; + break; + } + } +#endif + if (lostbits) + lostbits = 1; + else if (k > 0) + lostbits = any_on(b,k); +#ifdef IEEE_Arith + else if (check_denorm) + goto no_lostbits; +#endif + if (x[k>>kshift] & 1 << (k & kmask)) + lostbits |= 2; +#ifdef IEEE_Arith + no_lostbits: +#endif + nbits -= n; + rshift(b,n); + e = emin; + } + if (lostbits) { + up = 0; + switch(rounding) { + case Round_zero: + break; + case Round_near: + if (lostbits & 2 + && (lostbits & 1) | (x[0] & 1)) + up = 1; + break; + case Round_up: + up = 1 - sign; + break; + case Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b MTa); + x = b->x; + if (!denorm && (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n))) { + rshift(b,1); + if (++e > Emax) + goto ovfl; + } + } + } +#ifdef IEEE_Arith + if (denorm) + word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; + else { + normal: + word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); + } + word1(rvp) = b->x[0]; +#endif +#ifdef IBM + if ((j = e & 3)) { + k = b->x[0] & ((1 << j) - 1); + rshift(b,j); + if (k) { + switch(rounding) { + case Round_up: + if (!sign) + increment(b); + break; + case Round_down: + if (sign) + increment(b); + break; + case Round_near: + j = 1 << (j-1); + if (k & j && ((k & (j-1)) | lostbits)) + increment(b); + } + } + } + e >>= 2; + word0(rvp) = b->x[1] | ((e + 65 + 13) << 24); + word1(rvp) = b->x[0]; +#endif +#ifdef VAX + /* The next two lines ignore swap of low- and high-order 2 bytes. */ + /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */ + /* word1(rvp) = b->x[0]; */ + word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16); + word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16); +#endif + Bfree(b MTa); + } +#endif /*!NO_HEX_FP}*/ + + static int +dshift(Bigint *b, int p2) +{ + int rv = hi0bits(b->x[b->wds-1]) - 4; + if (p2 > 0) + rv -= p2; + return rv & kmask; + } + + static int +quorem(Bigint *b, Bigint *S) +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG +#ifdef NO_STRTOD_BIGCOMP + /*debug*/ if (q > 9) +#else + /* An oversized q is possible when quorem is called from bigcomp and */ + /* the input is near, e.g., twice the smallest denormalized number. */ + /*debug*/ if (q > 15) +#endif + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/ + static double +sulp(U *x, BCinfo *bc) +{ + U u; + double rv; + int i; + + rv = ulp(x); + if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) + return rv; /* Is there an example where i <= 0 ? */ + word0(&u) = Exp_1 + (i << Exp_shift); + word1(&u) = 0; + return rv * u.d; + } +#endif /*}*/ + +#ifndef NO_STRTOD_BIGCOMP + static void +bigcomp(U *rv, const char *s0, BCinfo *bc MTd) +{ + Bigint *b, *d; + int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase; + + dsign = bc->dsign; + nd = bc->nd; + nd0 = bc->nd0; + p5 = nd + bc->e0 - 1; + speccase = 0; +#ifndef Sudden_Underflow + if (rv->d == 0.) { /* special case: value near underflow-to-zero */ + /* threshold was rounded to zero */ + b = i2b(1 MTa); + p2 = Emin - P + 1; + bbits = 1; +#ifdef Avoid_Underflow + word0(rv) = (P+2) << Exp_shift; +#else + word1(rv) = 1; +#endif + i = 0; +#ifdef Honor_FLT_ROUNDS + if (bc->rounding == 1) +#endif + { + speccase = 1; + --p2; + dsign = 0; + goto have_i; + } + } + else +#endif + b = d2b(rv, &p2, &bbits MTa); +#ifdef Avoid_Underflow + p2 -= bc->scale; +#endif + /* floor(log2(rv)) == bbits - 1 + p2 */ + /* Check for denormal case. */ + i = P - bbits; + if (i > (j = P - Emin - 1 + p2)) { +#ifdef Sudden_Underflow + Bfree(b MTa); + b = i2b(1 MTa); + p2 = Emin; + i = P - 1; +#ifdef Avoid_Underflow + word0(rv) = (1 + bc->scale) << Exp_shift; +#else + word0(rv) = Exp_msk1; +#endif + word1(rv) = 0; +#else + i = j; +#endif + } +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (i > 0) + b = lshift(b, i MTa); + if (dsign) + b = increment(b MTa); + } + else +#endif + { + b = lshift(b, ++i MTa); + b->x[0] |= 1; + } +#ifndef Sudden_Underflow + have_i: +#endif + p2 -= p5 + i; + d = i2b(1 MTa); + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + */ + if (p5 > 0) + d = pow5mult(d, p5 MTa); + else if (p5 < 0) + b = pow5mult(b, -p5 MTa); + if (p2 > 0) { + b2 = p2; + d2 = 0; + } + else { + b2 = 0; + d2 = -p2; + } + i = dshift(d, d2); + if ((b2 += i) > 0) + b = lshift(b, b2 MTa); + if ((d2 += i) > 0) + d = lshift(d, d2 MTa); + + /* Now b/d = exactly half-way between the two floating-point values */ + /* on either side of the input string. Compute first digit of b/d. */ + + if (!(dig = quorem(b,d))) { + b = multadd(b, 10, 0 MTa); /* very unlikely */ + dig = quorem(b,d); + } + + /* Compare b/d with s0 */ + + for(i = 0; i < nd0; ) { + if ((dd = s0[i++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0 MTa); + dig = quorem(b,d); + } + for(j = bc->dp1; i++ < nd;) { + if ((dd = s0[j++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0 MTa); + dig = quorem(b,d); + } + if (dig > 0 || b->x[0] || b->wds > 1) + dd = -1; + ret: + Bfree(b MTa); + Bfree(d MTa); +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (dd < 0) { + if (bc->rounding == 0) { + if (!dsign) + goto retlow1; + } + else if (dsign) + goto rethi1; + } + else if (dd > 0) { + if (bc->rounding == 0) { + if (dsign) + goto rethi1; + goto ret1; + } + if (!dsign) + goto rethi1; + dval(rv) += 2.*sulp(rv,bc); + } + else { + bc->inexact = 0; + if (dsign) + goto rethi1; + } + } + else +#endif + if (speccase) { + if (dd <= 0) + rv->d = 0.; + } + else if (dd < 0) { + if (!dsign) /* does not happen for round-near */ +retlow1: + dval(rv) -= sulp(rv,bc); + } + else if (dd > 0) { + if (dsign) { + rethi1: + dval(rv) += sulp(rv,bc); + } + } + else { + /* Exact half-way case: apply round-even rule. */ + if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) { + i = 1 - j; + if (i <= 31) { + if (word1(rv) & (0x1 << i)) + goto odd; + } + else if (word0(rv) & (0x1 << (i-32))) + goto odd; + } + else if (word1(rv) & 1) { + odd: + if (dsign) + goto rethi1; + goto retlow1; + } + } + +#ifdef Honor_FLT_ROUNDS + ret1: +#endif + return; + } +#endif /* NO_STRTOD_BIGCOMP */ + + double +strtod__unused(const char *s00, char **se) +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1; + int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign; + const char *s, *s0, *s1; + double aadj, aadj1; + Long L; + U aadj2, adj, rv, rv0; + ULong y, z; + BCinfo bc; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef USE_BF96 + ULLong bhi, blo, brv, t00, t01, t02, t10, t11, terv, tg, tlo, yz; + const BF96 *p10; + int bexact, erv; +#endif +#ifdef Avoid_Underflow + ULong Lsb, Lsb1; +#endif +#ifdef SET_INEXACT + int oldinexact; +#endif +#ifndef NO_STRTOD_BIGCOMP + int req_bigcomp = 0; +#endif +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + bc.rounding = Flt_Rounds; +#else /*}{*/ + bc.rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: bc.rounding = 0; break; + case FE_UPWARD: bc.rounding = 2; break; + case FE_DOWNWARD: bc.rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ +#ifdef USE_LOCALE + const char *s2; +#endif + + sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0; + dval(&rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* fall through */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP /*{*/ + switch(s[1]) { + case 'x': + case 'X': +#ifdef Honor_FLT_ROUNDS + gethex(&s, &rv, bc.rounding, sign MTb); +#else + gethex(&s, &rv, 1, sign MTb); +#endif + goto ret; + } +#endif /*}*/ + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + nd = nf = 0; +#ifdef USE_BF96 + yz = 0; + for(; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 19) + yz = 10*yz + c - '0'; +#else + y = z = 0; + for(; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < DBL_DIG + 2) + z = 10*z + c - '0'; +#endif + nd0 = nd; + bc.dp0 = bc.dp1 = s - s0; + for(s1 = s; s1 > s0 && *--s1 == '0'; ) + ++nz1; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + bc.dp1 = s - s0; + bc.dplen = bc.dp1 - bc.dp0; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + bc.dp0 = s0 - s; + bc.dp1 = bc.dp0 + bc.dplen; + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + i = 1; +#ifdef USE_BF96 + for(; i < nz; ++i) { + if (++nd <= 19) + yz *= 10; + } + if (++nd <= 19) + yz = 10*yz + c; +#else + for(; i < nz; ++i) { + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 2) + z *= 10; + } + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 2) + z = 10*z + c; +#endif + nz = nz1 = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + /* fall through */ + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + while((c = *++s) >= '0' && c <= '9') { + if (L <= 19999) + L = 10*L + c - '0'; + } + if (L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK /*{*/ + /* Check for Nan and Infinity */ + if (!bc.dplen) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(&rv) = 0x7ff00000; + word1(&rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /*} INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + bc.e0 = e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; +#ifndef USE_BF96 + k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2; + dval(&rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(&rv) = tens[k - 9] * dval(&rv) + z; + } +#endif + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { +#ifdef USE_BF96 + dval(&rv) = yz; +#endif + if (!e) + goto ret; +#ifndef ROUND_BIASED_without_Round_Up + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef SET_INEXACT + bc.inexact = 0; + oldinexact = 1; +#endif +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef SET_INEXACT + bc.inexact = 0; + oldinexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + e -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(&rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if ((word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(&rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(&rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef SET_INEXACT + bc.inexact = 0; + oldinexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } +#endif +#endif /* ROUND_BIASED_without_Round_Up */ + } +#ifdef USE_BF96 + k = nd < 19 ? nd : 19; +#endif + e1 += nd - k; /* scale factor = 10^e1 */ + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + bc.inexact = 1; +#ifndef USE_BF96 + if (k <= DBL_DIG) +#endif + oldinexact = get_inexact(); +#endif +#ifdef Honor_FLT_ROUNDS + if (bc.rounding >= 2) { + if (sign) + bc.rounding = bc.rounding == 2 ? 0 : 2; + else + if (bc.rounding != 2) + bc.rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + +#ifdef USE_BF96 /*{*/ + Debug(++dtoa_stats[0]); + i = e1 + 342; + if (i < 0) + goto undfl; + if (i > 650) + goto ovfl; + p10 = &pten[i]; + brv = yz; + /* shift brv left, with i = number of bits shifted */ + i = 0; + if (!(brv & 0xffffffff00000000ull)) { + i = 32; + brv <<= 32; + } + if (!(brv & 0xffff000000000000ull)) { + i += 16; + brv <<= 16; + } + if (!(brv & 0xff00000000000000ull)) { + i += 8; + brv <<= 8; + } + if (!(brv & 0xf000000000000000ull)) { + i += 4; + brv <<= 4; + } + if (!(brv & 0xc000000000000000ull)) { + i += 2; + brv <<= 2; + } + if (!(brv & 0x8000000000000000ull)) { + i += 1; + brv <<= 1; + } + erv = (64 + 0x3fe) + p10->e - i; + if (erv <= 0 && nd > 19) + goto many_digits; /* denormal: may need to look at all digits */ + bhi = brv >> 32; + blo = brv & 0xffffffffull; + /* Unsigned 32-bit ints lie in [0,2^32-1] and */ + /* unsigned 64-bit ints lie in [0, 2^64-1]. The product of two unsigned */ + /* 32-bit ints is <= 2^64 - 2*2^32-1 + 1 = 2^64 - 1 - 2*(2^32 - 1), so */ + /* we can add two unsigned 32-bit ints to the product of two such ints, */ + /* and 64 bits suffice to contain the result. */ + t01 = bhi * p10->b1; + t10 = blo * p10->b0 + (t01 & 0xffffffffull); + t00 = bhi * p10->b0 + (t01 >> 32) + (t10 >> 32); + if (t00 & 0x8000000000000000ull) { + if ((t00 & 0x3ff) && (~t00 & 0x3fe)) { /* unambiguous result? */ + if (nd > 19 && ((t00 + (1< 19 && ((t00 + (1<b2; + t11 = blo * p10->b1 + (t02 & 0xffffffffull); + bexact = 1; + if (e1 < 0 || e1 > 41 || (t10 | t11) & 0xffffffffull || nd > 19) + bexact = 0; + tlo = (t10 & 0xffffffffull) + (t02 >> 32) + (t11 >> 32); + if (!bexact && (tlo + 0x10) >> 32 > tlo >> 32) + goto many_digits; + t00 += tlo >> 32; + if (t00 & 0x8000000000000000ull) { + if (erv <= 0) { /* denormal result */ + if (nd >= 20 || !((tlo & 0xfffffff0) | (t00 & 0x3ff))) + goto many_digits; + denormal: + if (erv <= -52) { +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: goto undfl; + case 2: goto tiniest; + } +#endif + if (erv < -52 || !(t00 & 0x7fffffffffffffffull)) + goto undfl; + goto tiniest; + } + tg = 1ull << (11 - erv); + t00 &= ~(tg - 1); /* clear low bits */ +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: goto noround_den; + case 2: goto roundup_den; + } +#endif + if (t00 & tg) { +#ifdef Honor_FLT_ROUNDS + roundup_den: +#endif + t00 += tg << 1; + if (!(t00 & 0x8000000000000000ull)) { + if (++erv > 0) + goto smallest_normal; + t00 = 0x8000000000000000ull; + } + } +#ifdef Honor_FLT_ROUNDS + noround_den: +#endif + LLval(&rv) = t00 >> (12 - erv); + Set_errno(ERANGE); + goto ret; + } + if (bexact) { +#ifdef SET_INEXACT + if (!(t00 & 0x7ff) && !(tlo & 0xffffffffull)) { + bc.inexact = 0; + goto noround; + } +#endif +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 2: + if (t00 & 0x7ff) + goto roundup; + case 0: goto noround; + } +#endif + if (t00 & 0x400 && (tlo & 0xffffffff) | (t00 & 0xbff)) + goto roundup; + goto noround; + } + if ((tlo & 0xfffffff0) | (t00 & 0x3ff) + && (nd <= 19 || ((t00 + (1ull << i)) & 0xfffffffffffffc00ull) + == (t00 & 0xfffffffffffffc00ull))) { + /* Unambiguous result. */ + /* If nd > 19, then incrementing the 19th digit */ + /* does not affect rv. */ +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: goto noround; + case 2: goto roundup; + } +#endif + if (t00 & 0x400) { /* round up */ + roundup: + t00 += 0x800; + if (!(t00 & 0x8000000000000000ull)) { + /* rounded up to a power of 2 */ + if (erv >= 0x7fe) + goto ovfl; + terv = erv + 1; + LLval(&rv) = terv << 52; + goto ret; + } + } + noround: + if (erv >= 0x7ff) + goto ovfl; + terv = erv; + LLval(&rv) = (terv << 52) | ((t00 & 0x7ffffffffffff800ull) >> 11); + goto ret; + } + } + else { + if (erv <= 1) { /* denormal result */ + if (nd >= 20 || !((tlo & 0xfffffff0) | (t00 & 0x1ff))) + goto many_digits; + denormal1: + if (erv <= -51) { +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: goto undfl; + case 2: goto tiniest; + } +#endif + if (erv < -51 || !(t00 & 0x3fffffffffffffffull)) + goto undfl; + tiniest: + LLval(&rv) = 1; + Set_errno(ERANGE); + goto ret; + } + tg = 1ull << (11 - erv); +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: goto noround1_den; + case 2: goto roundup1_den; + } +#endif + if (t00 & tg) { +#ifdef Honor_FLT_ROUNDS + roundup1_den: +#endif + if (0x8000000000000000ull & (t00 += (tg<<1)) && erv == 1) { + + smallest_normal: + LLval(&rv) = 0x0010000000000000ull; + goto ret; + } + } +#ifdef Honor_FLT_ROUNDS + noround1_den: +#endif + if (erv <= -52) + goto undfl; + LLval(&rv) = t00 >> (12 - erv); + Set_errno(ERANGE); + goto ret; + } + if (bexact) { +#ifdef SET_INEXACT + if (!(t00 & 0x3ff) && !(tlo & 0xffffffffull)) { + bc.inexact = 0; + goto noround1; + } +#endif +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 2: + if (t00 & 0x3ff) + goto roundup1; + case 0: goto noround1; + } +#endif + if (t00 & 0x200 && (t00 & 0x5ff || tlo)) + goto roundup1; + goto noround1; + } + if ((tlo & 0xfffffff0) | (t00 & 0x1ff) + && (nd <= 19 || ((t00 + (1ull << i)) & 0x7ffffffffffffe00ull) + == (t00 & 0x7ffffffffffffe00ull))) { + /* Unambiguous result. */ +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: goto noround1; + case 2: goto roundup1; + } +#endif + if (t00 & 0x200) { /* round up */ + roundup1: + t00 += 0x400; + if (!(t00 & 0x4000000000000000ull)) { + /* rounded up to a power of 2 */ + if (erv >= 0x7ff) + goto ovfl; + terv = erv; + LLval(&rv) = terv << 52; + goto ret; + } + } + noround1: + if (erv >= 0x800) + goto ovfl; + terv = erv - 1; + LLval(&rv) = (terv << 52) | ((t00 & 0x3ffffffffffffc00ull) >> 10); + goto ret; + } + } + many_digits: + Debug(++dtoa_stats[2]); + if (nd > 17) { + if (nd > 18) { + yz /= 100; + e1 += 2; + } + else { + yz /= 10; + e1 += 1; + } + y = yz / 100000000; + } + else if (nd > 9) { + i = nd - 9; + y = (yz >> i) / pfive[i-1]; + } + else + y = yz; + dval(&rv) = yz; +#endif /*}*/ + +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + bc.scale = 0; +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(&rv) = Big0; + word1(&rv) = Big1; + break; + default: + word0(&rv) = Exp_mask; + word1(&rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(&rv) = Exp_mask; + word1(&rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(&rv0) = 1e300; + dval(&rv0) *= dval(&rv0); +#endif +#else /*IEEE_Arith*/ + word0(&rv) = Big0; + word1(&rv) = Big1; +#endif /*IEEE_Arith*/ + range_err: + if (bd0) { + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(bd0 MTb); + Bfree(delta MTb); + } + Set_errno(ERANGE); + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(&rv) -= P*Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } + else + word0(&rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(&rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + bc.scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; clear j low bits */ + if (j >= 32) { + if (j > 54) + goto undfl; + word1(&rv) = 0; + if (j >= 53) + word0(&rv) = (P+2)*Exp_msk1; + else + word0(&rv) &= 0xffffffff << (j-32); + } + else + word1(&rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(&rv0) = dval(&rv); + dval(&rv) *= tinytens[j]; + if (!dval(&rv)) { + dval(&rv) = 2.*dval(&rv0); + dval(&rv) *= tinytens[j]; +#endif + if (!dval(&rv)) { + undfl: + dval(&rv) = 0.; +#ifdef Honor_FLT_ROUNDS + if (bc.rounding == 2) + word1(&rv) = 1; +#endif + goto range_err; + } +#ifndef Avoid_Underflow + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bc.nd = nd - nz1; +#ifndef NO_STRTOD_BIGCOMP + bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */ + /* to silence an erroneous warning about bc.nd0 */ + /* possibly not being initialized. */ + if (nd > strtod_diglim) { + /* ASSERT(strtod_diglim >= 18); 18 == one more than the */ + /* minimum number of decimal digits to distinguish double values */ + /* in IEEE arithmetic. */ + i = j = 18; + if (i > nd0) + j += bc.dplen; + for(;;) { + if (--j < bc.dp1 && j >= bc.dp0) + j = bc.dp0 - 1; + if (s0[j] != '0') + break; + --i; + } + e += nd - i; + nd = i; + if (nd0 > nd) + nd0 = nd; + if (nd < 9) { /* must recompute y */ + y = 0; + for(i = 0; i < nd0; ++i) + y = 10*y + s0[i] - '0'; + for(j = bc.dp1; i < nd; ++i) + y = 10*y + s0[j++] - '0'; + } + } +#endif + bd0 = s2b(s0, nd0, nd, y, bc.dplen MTb); + + for(;;) { + bd = Balloc(bd0->k MTb); + Bcopy(bd, bd0); + bb = d2b(&rv, &bbe, &bbbits MTb); /* rv = bb * 2^bbe */ + bs = i2b(1 MTb); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + Lsb = LSB; + Lsb1 = 0; + j = bbe - bc.scale; + i = j + bbbits - 1; /* logb(rv) */ + j = P + 1 - bbbits; + if (i < Emin) { /* denormal */ + i = Emin - i; + j -= i; + if (i < 32) + Lsb <<= i; + else if (i < 52) + Lsb1 = Lsb << (i-32); + else + Lsb1 = Exp_mask; + } +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += bc.scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5 MTb); + bb1 = mult(bs, bb MTb); + Bfree(bb MTb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2 MTb); + if (bd5 > 0) + bd = pow5mult(bd, bd5 MTb); + if (bd2 > 0) + bd = lshift(bd, bd2 MTb); + if (bs2 > 0) + bs = lshift(bs, bs2 MTb); + delta = diff(bb, bd MTb); + bc.dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifndef NO_STRTOD_BIGCOMP /*{*/ + if (bc.nd > nd && i <= 0) { + if (bc.dsign) { + /* Must use bigcomp(). */ + req_bigcomp = 1; + break; + } +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + if (i < 0) { + req_bigcomp = 1; + break; + } + } + else +#endif + i = -1; /* Discarded digits make delta smaller. */ + } +#endif /*}*/ +#ifdef Honor_FLT_ROUNDS /*{*/ + if (bc.rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + if (bc.rounding) { + if (bc.dsign) { + adj.d = 1.; + goto apply_adj; + } + } + else if (!bc.dsign) { + adj.d = -1.; + if (!word1(&rv) + && !(word0(&rv) & Frac_mask)) { + y = word0(&rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!bc.scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P MTb); + if (cmp(delta, bs) <= 0) + adj.d = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= + P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&rv) += adj.d*ulp(dval(&rv)); + word0(&rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + dval(&rv) += adj.d*ulp(&rv); + } + break; + } + adj.d = ratio(delta, bs); + if (adj.d < 1.) + adj.d = 1.; + if (adj.d <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj.d; + if (y != adj.d) { + if (!((bc.rounding>>1) ^ bc.dsign)) + y++; + adj.d = y; + } + } +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + adj.d *= ulp(dval(&rv)); + if (bc.dsign) + dval(&rv) += adj.d; + else + dval(&rv) -= adj.d; + word0(&rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + adj.d *= ulp(&rv); + if (bc.dsign) { + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + dval(&rv) += adj.d; + } + else + dval(&rv) -= adj.d; + goto cont; + } +#endif /*}Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask +#ifdef IEEE_Arith /*{*/ +#ifdef Avoid_Underflow + || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(&rv) & Exp_mask) <= Exp_msk1 +#endif +#endif /*}*/ + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + bc.inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P MTb); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (bc.dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( +#ifdef Avoid_Underflow + (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + word0(&rv) = (word0(&rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(&rv) = 0; +#ifdef Avoid_Underflow + bc.dsign = 0; +#endif + break; + } + } + else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(&rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (bc.scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(&rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else +#ifndef NO_STRTOD_BIGCOMP + if (bc.nd > nd) + goto cont; +#endif + break; +#endif + } +#ifndef ROUND_BIASED +#ifdef Avoid_Underflow + if (Lsb1) { + if (!(word0(&rv) & Lsb1)) + break; + } + else if (!(word1(&rv) & Lsb)) + break; +#else + if (!(word1(&rv) & LSB)) + break; +#endif +#endif + if (bc.dsign) +#ifdef Avoid_Underflow + dval(&rv) += sulp(&rv, &bc); +#else + dval(&rv) += ulp(&rv); +#endif +#ifndef ROUND_BIASED + else { +#ifdef Avoid_Underflow + dval(&rv) -= sulp(&rv, &bc); +#else + dval(&rv) -= ulp(&rv); +#endif +#ifndef Sudden_Underflow + if (!dval(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + } +#ifdef Avoid_Underflow + bc.dsign = 1 - bc.dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (bc.dsign) + aadj = aadj1 = 1.; + else if (word1(&rv) || word0(&rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(&rv) == Tiny1 && !word0(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = bc.dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(bc.rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) + aadj1 += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if ((word0(&rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) + goto ovfl; + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + else + word0(&rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (bc.scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) + z = 1; + aadj = z; + aadj1 = bc.dsign ? aadj : -aadj; + } + dval(&aadj2) = aadj1; + word0(&aadj2) += (2*P+1)*Exp_msk1 - y; + aadj1 = dval(&aadj2); + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if (rv.d == 0.) +#ifdef NO_STRTOD_BIGCOMP + goto undfl; +#else + { + req_bigcomp = 1; + break; + } +#endif + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + dval(&rv0) = dval(&rv); + word0(&rv) += P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#ifdef IBM + if ((word0(&rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(&rv0) == Tiny0 + && word1(&rv0) == Tiny1) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + goto cont; + } + else + word0(&rv) -= P*Exp_msk1; + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!bc.dsign) + aadj1 = -aadj1; + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(&rv) & Exp_mask; +#ifndef SET_INEXACT + if (bc.nd == nd) { +#ifdef Avoid_Underflow + if (!bc.scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + } +#endif + cont: + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(delta MTb); + } + Bfree(bb MTb); + Bfree(bd MTb); + Bfree(bs MTb); + Bfree(bd0 MTb); + Bfree(delta MTb); +#ifndef NO_STRTOD_BIGCOMP + if (req_bigcomp) { + bd0 = 0; + bc.e0 += nz1; + bigcomp(&rv, s0, &bc MTb); + y = word0(&rv) & Exp_mask; + if (y == Exp_mask) + goto ovfl; + if (y == 0 && rv.d == 0.) + goto undfl; + } +#endif +#ifdef Avoid_Underflow + if (bc.scale) { + word0(&rv0) = Exp_1 - 2*P*Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ +#ifdef IEEE_Arith + if (!(word0(&rv) & Exp_mask)) +#else + if (word0(&rv) == 0 && word1(&rv) == 0) +#endif + Set_errno(ERANGE); +#endif + } +#endif /* Avoid_Underflow */ + ret: +#ifdef SET_INEXACT + if (bc.inexact) { + if (!(word0(&rv) & Exp_mask)) { + /* set underflow and inexact bits */ + dval(&rv0) = 1e-300; + dval(&rv0) *= dval(&rv0); + } + else if (!oldinexact) { + word0(&rv0) = Exp_1 + (70 << Exp_shift); + word1(&rv0) = 0; + dval(&rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + if (se) + *se = (char *)s; + return sign ? -dval(&rv) : dval(&rv); + } + +#ifndef MULTIPLE_THREADS + static char *dtoa_result; +#endif + + static char * +rv_alloc(int i MTd) +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i; + j <<= 1) + k++; + r = (int*)Balloc(k MTa); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + static char * +nrv_alloc(const char *s, char *s0, size_t s0len, char **rve, int n MTd) +{ + char *rv, *t; + + if (!s0) + s0 = rv_alloc(n MTa); + else if (s0len <= (size_t)n) { + rv = 0; + t = rv + n; + goto rve_chk; + } + t = rv = s0; + while((*t = *s++)) + ++t; + rve_chk: + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +freedtoa(char *s) +{ +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b MTb); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + char * +dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve, char *buf, size_t blen) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + When not NULL, buf is an output buffer of length blen, which must + be large enough to accommodate suppressed trailing zeros and a trailing + null byte. If blen is too small, rv = NULL is returned, in which case + if rve is not NULL, a subsequent call with blen >= (*rve - rv) + 1 + should succeed in returning buf. + + When buf is NULL, sufficient space is allocated for the return value, + which, when done using, the caller should pass to freedtoa(). + + USE_BF is automatically defined when neither NO_LONG_LONG nor NO_BF96 + is defined. + */ + +#ifdef MULTIPLE_THREADS + ThInfo *TI = 0; +#endif + int bbits, b2, b5, be, dig, i, ilim, ilim1, + j, j1, k, leftright, m2, m5, s2, s5, spec_case; +#if !defined(Sudden_Underflow) || defined(USE_BF96) + int denorm; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U u; + char *s; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef USE_BF96 /*{{*/ + BF96 *p10; + ULLong dbhi, dbits, dblo, den, hb, rb, rblo, res, res0, res3, reslo, sres, + sulp, tv0, tv1, tv2, tv3, ulp, ulplo, ulpmask, ures, ureslo, zb; + int eulp, k1, n2, ulpadj, ulpshift; +#else /*}{*/ +#ifndef Sudden_Underflow + ULong x; +#endif + Long L; + U d2, eps; + double ds; + int ieps, ilim0, k0, k_check, try_quick; +#ifndef No_leftright +#ifdef IEEE_Arith + U eps1; +#endif +#endif +#endif /*}}*/ +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + + u.d = dd; + if (word0(&u) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&u) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(&u) & Exp_mask) == Exp_mask) +#else + if (word0(&u) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(&u) && !(word0(&u) & 0xfffff)) + return nrv_alloc("Infinity", buf, blen, rve, 8 MTb); +#endif + return nrv_alloc("NaN", buf, blen, rve, 3 MTb); + } +#endif +#ifdef IBM + dval(&u) += 0; /* normalize */ +#endif + if (!dval(&u)) { + *decpt = 1; + return nrv_alloc("0", buf, blen, rve, 1 MTb); + } + +#ifdef SET_INEXACT +#ifndef USE_BF96 + try_quick = +#endif + oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif +#ifdef USE_BF96 /*{{*/ + dbits = (u.LL & 0xfffffffffffffull) << 11; /* fraction bits */ + if ((be = u.LL >> 52)) /* biased exponent; nonzero ==> normal */ { + dbits |= 0x8000000000000000ull; + denorm = ulpadj = 0; + } + else { + denorm = 1; + ulpadj = be + 1; + dbits <<= 1; + if (!(dbits & 0xffffffff00000000ull)) { + dbits <<= 32; + be -= 32; + } + if (!(dbits & 0xffff000000000000ull)) { + dbits <<= 16; + be -= 16; + } + if (!(dbits & 0xff00000000000000ull)) { + dbits <<= 8; + be -= 8; + } + if (!(dbits & 0xf000000000000000ull)) { + dbits <<= 4; + be -= 4; + } + if (!(dbits & 0xc000000000000000ull)) { + dbits <<= 2; + be -= 2; + } + if (!(dbits & 0x8000000000000000ull)) { + dbits <<= 1; + be -= 1; + } + assert(be >= -51); + ulpadj -= be; + } + j = Lhint[be + 51]; + p10 = &pten[j]; + dbhi = dbits >> 32; + dblo = dbits & 0xffffffffull; + i = be - 0x3fe; + if (i < p10->e + || (i == p10->e && (dbhi < p10->b0 || (dbhi == p10->b0 && dblo < p10->b1)))) + --j; + k = j - 342; + + /* now 10^k <= dd < 10^(k+1) */ + +#else /*}{*/ + + b = d2b(&u, &be, &bbits MTb); +#ifdef Sudden_Underflow + i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) + dval(&d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } +#endif /*}}*/ + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef USE_BF96 +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ +#endif /*USE_BF96*/ + + if (mode > 5) { + mode -= 4; +#ifndef USE_BF96 + try_quick = 0; +#endif + } + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* fall through */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* fall through */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + if (!buf) { + buf = rv_alloc(i MTb); + blen = sizeof(Bigint) + ((1 << ((int*)buf)[-1]) - 1)*sizeof(ULong) - sizeof(int); + } + else if (blen <= (size_t)i) { + buf = 0; + if (rve) + *rve = buf + i; + return buf; + } + s = buf; + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if (mode < 2 || (leftright +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + )) { + if (!word1(&u) && !(word0(&u) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&u) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + spec_case = 1; + } + } + +#ifdef USE_BF96 /*{*/ + b = 0; + if (ilim < 0 && (mode == 3 || mode == 5)) { + S = mhi = 0; + goto no_digits; + } + i = 1; + j = 52 + 0x3ff - be; + ulpshift = 0; + ulplo = 0; + /* Can we do an exact computation with 64-bit integer arithmetic? */ + if (k < 0) { + if (k < -25) + goto toobig; + res = dbits >> 11; + n2 = pfivebits[k1 = -(k + 1)] + 53; + j1 = j; + if (n2 > 61) { + ulpshift = n2 - 61; + if (res & (ulpmask = (1ull << ulpshift) - 1)) + goto toobig; + j -= ulpshift; + res >>= ulpshift; + } + /* Yes. */ + res *= ulp = pfive[k1]; + if (ulpshift) { + ulplo = ulp; + ulp >>= ulpshift; + } + j += k; + if (ilim == 0) { + S = mhi = 0; + if (res > (5ull << j)) + goto one_digit; + goto no_digits; + } + goto no_div; + } + if (ilim == 0 && j + k >= 0) { + S = mhi = 0; + if ((dbits >> 11) > (pfive[k-1] << j)) + goto one_digit; + goto no_digits; + } + if (k <= dtoa_divmax && j + k >= 0) { + /* Another "yes" case -- we will use exact integer arithmetic. */ + use_exact: + Debug(++dtoa_stats[3]); + res = dbits >> 11; /* residual */ + ulp = 1; + if (k <= 0) + goto no_div; + j1 = j + k + 1; + den = pfive[k-i] << (j1 - i); + for(;;) { + dig = res / den; + *s++ = '0' + dig; + if (!(res -= dig*den)) { +#ifdef SET_INEXACT + inexact = 0; + oldinexact = 1; +#endif + goto retc; + } + if (ilim < 0) { + ures = den - res; + if (2*res <= ulp + && (spec_case ? 4*res <= ulp : (2*res < ulp || dig & 1))) + goto ulp_reached; + if (2*ures < ulp) + goto Roundup; + } + else if (i == ilim) { + switch(Rounding) { + case 0: goto retc; + case 2: goto Roundup; + } + ures = 2*res; + if (ures > den + || (ures == den && dig & 1) + || (spec_case && res <= ulp && 2*res >= ulp)) + goto Roundup; + goto retc; + } + if (j1 < ++i) { + res *= 10; + ulp *= 10; + } + else { + if (i > k) + break; + den = pfive[k-i] << (j1 - i); + } + } + no_div: + for(;;) { + dig = den = res >> j; + *s++ = '0' + dig; + if (!(res -= den << j)) { +#ifdef SET_INEXACT + inexact = 0; + oldinexact = 1; +#endif + goto retc; + } + if (ilim < 0) { + ures = (1ull << j) - res; + if (2*res <= ulp + && (spec_case ? 4*res <= ulp : (2*res < ulp || dig & 1))) { + ulp_reached: + if (ures < res + || (ures == res && dig & 1)) + goto Roundup; + goto retc; + } + if (2*ures < ulp) + goto Roundup; + } + --j; + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: goto retc; + case 2: goto Roundup; + } +#endif + hb = 1ull << j; + if (res & hb && (dig & 1 || res & (hb-1))) + goto Roundup; + if (spec_case && res <= ulp && 2*res >= ulp) { + Roundup: + while(*--s == '9') + if (s == buf) { + ++k; + *s++ = '1'; + goto ret1; + } + ++*s++; + goto ret1; + } + goto retc; + } + ++i; + res *= 5; + if (ulpshift) { + ulplo = 5*(ulplo & ulpmask); + ulp = 5*ulp + (ulplo >> ulpshift); + } + else + ulp *= 5; + } + } + toobig: + if (ilim > 28) + goto Fast_failed1; + /* Scale by 10^-k */ + p10 = &pten[342-k]; + tv0 = p10->b2 * dblo; /* rarely matters, but does, e.g., for 9.862818194192001e18 */ + tv1 = p10->b1 * dblo + (tv0 >> 32); + tv2 = p10->b2 * dbhi + (tv1 & 0xffffffffull); + tv3 = p10->b0 * dblo + (tv1>>32) + (tv2>>32); + res3 = p10->b1 * dbhi + (tv3 & 0xffffffffull); + res = p10->b0 * dbhi + (tv3>>32) + (res3>>32); + be += p10->e - 0x3fe; + eulp = j1 = be - 54 + ulpadj; + if (!(res & 0x8000000000000000ull)) { + --be; + res3 <<= 1; + res = (res << 1) | ((res3 & 0x100000000ull) >> 32); + } + res0 = res; /* save for Fast_failed */ +#if !defined(SET_INEXACT) && !defined(NO_DTOA_64) /*{*/ + if (ilim > 19) + goto Fast_failed; + Debug(++dtoa_stats[4]); + assert(be >= 0 && be <= 4); /* be = 0 is rare, but possible, e.g., for 1e20 */ + res >>= 4 - be; + ulp = p10->b0; /* ulp */ + ulp = (ulp << 29) | (p10->b1 >> 3); + /* scaled ulp = ulp * 2^(eulp - 60) */ + /* We maintain 61 bits of the scaled ulp. */ + if (ilim == 0) { + if (!(res & 0x7fffffffffffffeull) + || !((~res) & 0x7fffffffffffffeull)) + goto Fast_failed1; + S = mhi = 0; + if (res >= 0x5000000000000000ull) + goto one_digit; + goto no_digits; + } + rb = 1; /* upper bound on rounding error */ + for(;;++i) { + dig = res >> 60; + *s++ = '0' + dig; + res &= 0xfffffffffffffffull; + if (ilim < 0) { + ures = 0x1000000000000000ull - res; + if (eulp > 0) { + assert(eulp <= 4); + sulp = ulp << (eulp - 1); + if (res <= ures) { + if (res + rb > ures - rb) + goto Fast_failed; + if (res < sulp) + goto retc; + } + else { + if (res - rb <= ures + rb) + goto Fast_failed; + if (ures < sulp) + goto Roundup; + } + } + else { + zb = -(1ull << (eulp + 63)); + if (!(zb & res)) { + sres = res << (1 - eulp); + if (sres < ulp && (!spec_case || 2*sres < ulp)) { + if ((res+rb) << (1 - eulp) >= ulp) + goto Fast_failed; + if (ures < res) { + if (ures + rb >= res - rb) + goto Fast_failed; + goto Roundup; + } + if (ures - rb < res + rb) + goto Fast_failed; + goto retc; + } + } + if (!(zb & ures) && ures << -eulp < ulp) { + if (ures << (1 - eulp) < ulp) + goto Roundup; + goto Fast_failed; + } + } + } + else if (i == ilim) { + ures = 0x1000000000000000ull - res; + if (ures < res) { + if (ures <= rb || res - rb <= ures + rb) { + if (j + k >= 0 && k >= 0 && k <= 27) + goto use_exact1; + goto Fast_failed; + } +#ifdef Honor_FLT_ROUNDS + if (Rounding == 0) + goto retc; +#endif + goto Roundup; + } + if (res <= rb || ures - rb <= res + rb) { + if (j + k >= 0 && k >= 0 && k <= 27) { + use_exact1: + s = buf; + i = 1; + goto use_exact; + } + goto Fast_failed; + } +#ifdef Honor_FLT_ROUNDS + if (Rounding == 2) + goto Roundup; +#endif + goto retc; + } + rb *= 10; + if (rb >= 0x1000000000000000ull) + goto Fast_failed; + res *= 10; + ulp *= 5; + if (ulp & 0x8000000000000000ull) { + eulp += 4; + ulp >>= 3; + } + else { + eulp += 3; + ulp >>= 2; + } + } +#endif /*}*/ +#ifndef NO_BF96 + Fast_failed: +#endif + Debug(++dtoa_stats[5]); + s = buf; + i = 4 - be; + res = res0 >> i; + reslo = 0xffffffffull & res3; + if (i) + reslo = (res0 << (64 - i)) >> 32 | (reslo >> i); + rb = 0; + rblo = 4; /* roundoff bound */ + ulp = p10->b0; /* ulp */ + ulp = (ulp << 29) | (p10->b1 >> 3); + eulp = j1; + for(i = 1;;++i) { + dig = res >> 60; + *s++ = '0' + dig; + res &= 0xfffffffffffffffull; +#ifdef SET_INEXACT + if (!res && !reslo) { + if (!(res3 & 0xffffffffull)) { + inexact = 0; + oldinexact = 1; + } + goto retc; + } +#endif + if (ilim < 0) { + ures = 0x1000000000000000ull - res; + ureslo = 0; + if (reslo) { + ureslo = 0x100000000ull - reslo; + --ures; + } + if (eulp > 0) { + assert(eulp <= 4); + sulp = (ulp << (eulp - 1)) - rb; + if (res <= ures) { + if (res < sulp) { + if (res+rb < ures-rb) + goto retc; + } + } + else if (ures < sulp) { + if (res-rb > ures+rb) + goto Roundup; + } + goto Fast_failed1; + } + else { + zb = -(1ull << (eulp + 60)); + if (!(zb & (res + rb))) { + sres = (res - rb) << (1 - eulp); + if (sres < ulp && (!spec_case || 2*sres < ulp)) { + sres = res << (1 - eulp); + if ((j = eulp + 31) > 0) + sres += (rblo + reslo) >> j; + else + sres += (rblo + reslo) << -j; + if (sres + (rb << (1 - eulp)) >= ulp) + goto Fast_failed1; + if (sres >= ulp) + goto more96; + if (ures < res + || (ures == res && ureslo < reslo)) { + if (ures + rb >= res - rb) + goto Fast_failed1; + goto Roundup; + } + if (ures - rb <= res + rb) + goto Fast_failed1; + goto retc; + } + } + if (!(zb & ures) && (ures-rb) << (1 - eulp) < ulp) { + if ((ures + rb) << (1 - eulp) < ulp) + goto Roundup; + goto Fast_failed1; + } + } + } + else if (i == ilim) { + ures = 0x1000000000000000ull - res; + sres = ureslo = 0; + if (reslo) { + ureslo = 0x100000000ull - reslo; + --ures; + sres = (reslo + rblo) >> 31; + } + sres += 2*rb; + if (ures <= res) { + if (ures <=sres || res - ures <= sres) + goto Fast_failed1; +#ifdef Honor_FLT_ROUNDS + if (Rounding == 0) + goto retc; +#endif + goto Roundup; + } + if (res <= sres || ures - res <= sres) + goto Fast_failed1; +#ifdef Honor_FLT_ROUNDS + if (Rounding == 2) + goto Roundup; +#endif + goto retc; + } + more96: + rblo *= 10; + rb = 10*rb + (rblo >> 32); + rblo &= 0xffffffffull; + if (rb >= 0x1000000000000000ull) + goto Fast_failed1; + reslo *= 10; + res = 10*res + (reslo >> 32); + reslo &= 0xffffffffull; + ulp *= 5; + if (ulp & 0x8000000000000000ull) { + eulp += 4; + ulp >>= 3; + } + else { + eulp += 3; + ulp >>= 2; + } + } + Fast_failed1: + Debug(++dtoa_stats[6]); + S = mhi = mlo = 0; +#ifdef USE_BF96 + b = d2b(&u, &be, &bbits MTb); +#endif + s = buf; + i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); + i -= Bias; + if (ulpadj) + i -= ulpadj - 1; + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } +#endif /*}*/ + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && Rounding != 1) + leftright = 0; +#endif + +#ifndef USE_BF96 /*{*/ + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + j1 = -(k0 = k); + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&u) /= ds; + } + else if (j1 > 0) { + dval(&u) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&u) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) + goto one_digit; + if (dval(&u) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); +#ifdef IEEE_Arith + if (j1 >= 307) { + eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ + word0(&eps1) -= Exp_msk1 * (Bias+P-1); + dval(&eps1) *= tens[j1 & 0xf]; + for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) + if (j & 1) + dval(&eps1) *= bigtens[i]; + if (eps.d < eps1.d) + eps.d = eps1.d; + if (10. - u.d < 10.*eps.d && eps.d < 1.) { + /* eps.d < 1. excludes trouble with the tiniest denormal */ + *s++ = '1'; + ++k; + goto ret1; + } + } +#endif + for(i = 0;;) { + L = dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (1. - dval(&u) < dval(&eps)) + goto bump_up; + if (dval(&u) < dval(&eps)) + goto retc; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&u) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u)); + if (!(dval(&u) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) + goto bump_up; + else if (dval(&u) < 0.5 - dval(&eps)) + goto retc; + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = buf; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&u) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u) / ds); + dval(&u) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&u) < 0) { + L--; + dval(&u) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&u)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto retc; + case 2: goto bump_up; + } +#endif + dval(&u) += dval(&u); +#ifdef ROUND_BIASED + if (dval(&u) >= ds) +#else + if (dval(&u) > ds || (dval(&u) == ds && L & 1)) +#endif + { + bump_up: + while(*--s == '9') + if (s == buf) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto retc; + } + +#endif /*}*/ + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1 MTb); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5 MTb); + b1 = mult(mhi, b MTb); + Bfree(b MTb); + b = b1; + } + if ((j = b5 - m5)) + b = pow5mult(b, j MTb); + } + else + b = pow5mult(b, b5 MTb); + } + S = i2b(1 MTb); + if (s5 > 0) + S = pow5mult(S, s5 MTb); + + if (spec_case) { + b2 += Log2P; + s2 += Log2P; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + i = dshift(S, s2); + b2 += i; + m2 += i; + s2 += i; + if (b2 > 0) + b = lshift(b, b2 MTb); + if (s2 > 0) + S = lshift(S, s2 MTb); +#ifndef USE_BF96 + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0 MTb); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0 MTb); + ilim = ilim1; + } + } +#endif + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0 MTb)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + ++k; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2 MTb); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k MTb); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P MTb); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi MTb); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta MTb); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(&u) & 1) +#ifdef Honor_FLT_ROUNDS + && (mode <= 1 || Rounding >= 1) +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&u) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1 MTb); + j1 = cmp(b, S); +#ifdef ROUND_BIASED + if (j1 >= 0 /*)*/ +#else + if ((j1 > 0 || (j1 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding && mode > 1) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0 MTb); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0 MTb); + else { + mlo = multadd(mlo, 10, 0 MTb); + mhi = multadd(mhi, 10, 0 MTb); + } + } + } + else + for(i = 1;; i++) { + dig = quorem(b,S) + '0'; + *s++ = dig; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0 MTb); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto ret; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1 MTb); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + while(*--s == '9') + if (s == buf) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + ret: + Bfree(S MTb); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo MTb); + Bfree(mhi MTb); + } + retc: + while(s > buf && s[-1] == '0') + --s; + ret1: + if (b) + Bfree(b MTb); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&u) = Exp_1 + (70 << Exp_shift); + word1(&u) = 0; + dval(&u) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + return buf; + } + + char * +dtoa(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) +{ + /* Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + See dtoa_r() above for details on the other arguments. + */ +#ifndef MULTIPLE_THREADS + if (dtoa_result) + freedtoa(dtoa_result); +#endif + return dtoa_r(dd, mode, ndigits, decpt, sign, rve, 0, 0); + } + +#ifdef __cplusplus +} +#endif diff --git a/lib/jansson/src/dump.c b/lib/jansson/src/dump.c new file mode 100644 index 0000000..28ff692 --- /dev/null +++ b/lib/jansson/src/dump.c @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson_private.h" + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "jansson.h" +#include "strbuffer.h" +#include "utf.h" + +#define MAX_INTEGER_STR_LENGTH 25 +#define MAX_REAL_STR_LENGTH 25 + +#define FLAGS_TO_INDENT(f) ((f)&0x1F) +#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) + +struct buffer { + const size_t size; + size_t used; + char *data; +}; + +static int dump_to_strbuffer(const char *buffer, size_t size, void *data) { + return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); +} + +static int dump_to_buffer(const char *buffer, size_t size, void *data) { + struct buffer *buf = (struct buffer *)data; + + if (buf->used + size <= buf->size) + memcpy(&buf->data[buf->used], buffer, size); + + buf->used += size; + return 0; +} + +static int dump_to_file(const char *buffer, size_t size, void *data) { + FILE *dest = (FILE *)data; + if (fwrite(buffer, size, 1, dest) != 1) + return -1; + return 0; +} + +static int dump_to_fd(const char *buffer, size_t size, void *data) { +#ifdef HAVE_UNISTD_H + int *dest = (int *)data; + if (write(*dest, buffer, size) == (ssize_t)size) + return 0; +#endif + return -1; +} + +/* 32 spaces (the maximum indentation size) */ +static const char whitespace[] = " "; + +static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, + void *data) { + if (FLAGS_TO_INDENT(flags) > 0) { + unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count; + + if (dump("\n", 1, data)) + return -1; + + while (n_spaces > 0) { + int cur_n = + n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1; + + if (dump(whitespace, cur_n, data)) + return -1; + + n_spaces -= cur_n; + } + } else if (space && !(flags & JSON_COMPACT)) { + return dump(" ", 1, data); + } + return 0; +} + +static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, + size_t flags) { + const char *pos, *end, *lim; + int32_t codepoint = 0; + + if (dump("\"", 1, data)) + return -1; + + end = pos = str; + lim = str + len; + while (1) { + const char *text; + char seq[13]; + int length; + + while (end < lim) { + end = utf8_iterate(pos, lim - pos, &codepoint); + if (!end) + return -1; + + /* mandatory escape or control char */ + if (codepoint == '\\' || codepoint == '"' || codepoint < 0x20) + break; + + /* slash */ + if ((flags & JSON_ESCAPE_SLASH) && codepoint == '/') + break; + + /* non-ASCII */ + if ((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) + break; + + pos = end; + } + + if (pos != str) { + if (dump(str, pos - str, data)) + return -1; + } + + if (end == pos) + break; + + /* handle \, /, ", and control codes */ + length = 2; + switch (codepoint) { + case '\\': + text = "\\\\"; + break; + case '\"': + text = "\\\""; + break; + case '\b': + text = "\\b"; + break; + case '\f': + text = "\\f"; + break; + case '\n': + text = "\\n"; + break; + case '\r': + text = "\\r"; + break; + case '\t': + text = "\\t"; + break; + case '/': + text = "\\/"; + break; + default: { + /* codepoint is in BMP */ + if (codepoint < 0x10000) { + snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint); + length = 6; + } + + /* not in BMP -> construct a UTF-16 surrogate pair */ + else { + int32_t first, last; + + codepoint -= 0x10000; + first = 0xD800 | ((codepoint & 0xffc00) >> 10); + last = 0xDC00 | (codepoint & 0x003ff); + + snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, + (unsigned int)last); + length = 12; + } + + text = seq; + break; + } + } + + if (dump(text, length, data)) + return -1; + + str = pos = end; + } + + return dump("\"", 1, data); +} + +struct key_len { + const char *key; + int len; +}; + +static int compare_keys(const void *key1, const void *key2) { + const struct key_len *k1 = key1; + const struct key_len *k2 = key2; + const size_t min_size = k1->len < k2->len ? k1->len : k2->len; + int res = memcmp(k1->key, k2->key, min_size); + + if (res) + return res; + + return k1->len - k2->len; +} + +static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents, + json_dump_callback_t dump, void *data) { + int embed = flags & JSON_EMBED; + + flags &= ~JSON_EMBED; + + if (!json) + return -1; + + switch (json_typeof(json)) { + case JSON_NULL: + return dump("null", 4, data); + + case JSON_TRUE: + return dump("true", 4, data); + + case JSON_FALSE: + return dump("false", 5, data); + + case JSON_INTEGER: { + char buffer[MAX_INTEGER_STR_LENGTH]; + int size; + + size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT, + json_integer_value(json)); + if (size < 0 || size >= MAX_INTEGER_STR_LENGTH) + return -1; + + return dump(buffer, size, data); + } + + case JSON_REAL: { + char buffer[MAX_REAL_STR_LENGTH]; + int size; + double value = json_real_value(json); + + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, + FLAGS_TO_PRECISION(flags)); + if (size < 0) + return -1; + + return dump(buffer, size, data); + } + + case JSON_STRING: + return dump_string(json_string_value(json), json_string_length(json), dump, + data, flags); + + case JSON_ARRAY: { + size_t n; + size_t i; + /* Space for "0x", double the sizeof a pointer for the hex and a + * terminator. */ + char key[2 + (sizeof(json) * 2) + 1]; + size_t key_len; + + /* detect circular references */ + if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len)) + return -1; + + n = json_array_size(json); + + if (!embed && dump("[", 1, data)) + return -1; + if (n == 0) { + hashtable_del(parents, key, key_len); + return embed ? 0 : dump("]", 1, data); + } + if (dump_indent(flags, depth + 1, 0, dump, data)) + return -1; + + for (i = 0; i < n; ++i) { + if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump, + data)) + return -1; + + if (i < n - 1) { + if (dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } else { + if (dump_indent(flags, depth, 0, dump, data)) + return -1; + } + } + + hashtable_del(parents, key, key_len); + return embed ? 0 : dump("]", 1, data); + } + + case JSON_OBJECT: { + void *iter; + const char *separator; + int separator_length; + char loop_key[LOOP_KEY_LEN]; + size_t loop_key_len; + + if (flags & JSON_COMPACT) { + separator = ":"; + separator_length = 1; + } else { + separator = ": "; + separator_length = 2; + } + + /* detect circular references */ + if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key), + &loop_key_len)) + return -1; + + iter = json_object_iter((json_t *)json); + + if (!embed && dump("{", 1, data)) + return -1; + if (!iter) { + hashtable_del(parents, loop_key, loop_key_len); + return embed ? 0 : dump("}", 1, data); + } + if (dump_indent(flags, depth + 1, 0, dump, data)) + return -1; + + if (flags & JSON_SORT_KEYS) { + struct key_len *keys; + size_t size, i; + + size = json_object_size(json); + keys = jsonp_malloc(size * sizeof(struct key_len)); + if (!keys) + return -1; + + i = 0; + while (iter) { + struct key_len *keylen = &keys[i]; + + keylen->key = json_object_iter_key(iter); + keylen->len = json_object_iter_key_len(iter); + + iter = json_object_iter_next((json_t *)json, iter); + i++; + } + assert(i == size); + + qsort(keys, size, sizeof(struct key_len), compare_keys); + + for (i = 0; i < size; i++) { + const struct key_len *key; + json_t *value; + + key = &keys[i]; + value = json_object_getn(json, key->key, key->len); + assert(value); + + dump_string(key->key, key->len, dump, data, flags); + if (dump(separator, separator_length, data) || + do_dump(value, flags, depth + 1, parents, dump, data)) { + jsonp_free(keys); + return -1; + } + + if (i < size - 1) { + if (dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) { + jsonp_free(keys); + return -1; + } + } else { + if (dump_indent(flags, depth, 0, dump, data)) { + jsonp_free(keys); + return -1; + } + } + } + + jsonp_free(keys); + } else { + /* Don't sort keys */ + + while (iter) { + void *next = json_object_iter_next((json_t *)json, iter); + const char *key = json_object_iter_key(iter); + const size_t key_len = json_object_iter_key_len(iter); + + dump_string(key, key_len, dump, data, flags); + if (dump(separator, separator_length, data) || + do_dump(json_object_iter_value(iter), flags, depth + 1, parents, + dump, data)) + return -1; + + if (next) { + if (dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } else { + if (dump_indent(flags, depth, 0, dump, data)) + return -1; + } + + iter = next; + } + } + + hashtable_del(parents, loop_key, loop_key_len); + return embed ? 0 : dump("}", 1, data); + } + + default: + /* not reached */ + return -1; + } +} + +char *json_dumps(const json_t *json, size_t flags) { + strbuffer_t strbuff; + char *result; + + if (strbuffer_init(&strbuff)) + return NULL; + + if (json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) + result = NULL; + else + result = jsonp_strdup(strbuffer_value(&strbuff)); + + strbuffer_close(&strbuff); + return result; +} + +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) { + struct buffer buf = {size, 0, buffer}; + + if (json_dump_callback(json, dump_to_buffer, (void *)&buf, flags)) + return 0; + + return buf.used; +} + +int json_dumpf(const json_t *json, FILE *output, size_t flags) { + return json_dump_callback(json, dump_to_file, (void *)output, flags); +} + +int json_dumpfd(const json_t *json, int output, size_t flags) { + return json_dump_callback(json, dump_to_fd, (void *)&output, flags); +} + +int json_dump_file(const json_t *json, const char *path, size_t flags) { + int result; + + FILE *output = fopen(path, "w"); + if (!output) + return -1; + + result = json_dumpf(json, output, flags); + + if (fclose(output) != 0) + return -1; + + return result; +} + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, + size_t flags) { + int res; + hashtable_t parents_set; + + if (!(flags & JSON_ENCODE_ANY)) { + if (!json_is_array(json) && !json_is_object(json)) + return -1; + } + + if (hashtable_init(&parents_set)) + return -1; + res = do_dump(json, flags, 0, &parents_set, callback, data); + hashtable_close(&parents_set); + + return res; +} diff --git a/lib/jansson/src/error.c b/lib/jansson/src/error.c new file mode 100644 index 0000000..14d0047 --- /dev/null +++ b/lib/jansson/src/error.c @@ -0,0 +1,59 @@ +#include "jansson_private.h" +#include + +void jsonp_error_init(json_error_t *error, const char *source) { + if (error) { + error->text[0] = '\0'; + error->line = -1; + error->column = -1; + error->position = 0; + if (source) + jsonp_error_set_source(error, source); + else + error->source[0] = '\0'; + } +} + +void jsonp_error_set_source(json_error_t *error, const char *source) { + size_t length; + + if (!error || !source) + return; + + length = strlen(source); + if (length < JSON_ERROR_SOURCE_LENGTH) + strncpy(error->source, source, length + 1); + else { + size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; + memcpy(error->source, "...", 3); + strncpy(error->source + 3, source + extra, length - extra + 1); + } +} + +void jsonp_error_set(json_error_t *error, int line, int column, size_t position, + enum json_error_code code, const char *msg, ...) { + va_list ap; + + va_start(ap, msg); + jsonp_error_vset(error, line, column, position, code, msg, ap); + va_end(ap); +} + +void jsonp_error_vset(json_error_t *error, int line, int column, size_t position, + enum json_error_code code, const char *msg, va_list ap) { + if (!error) + return; + + if (error->text[0] != '\0') { + /* error already set */ + return; + } + + error->line = line; + error->column = column; + error->position = (int)position; + + vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap); + error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0'; + error->text[JSON_ERROR_TEXT_LENGTH - 1] = code; +} diff --git a/lib/jansson/src/hashtable.c b/lib/jansson/src/hashtable.c new file mode 100644 index 0000000..5e97f85 --- /dev/null +++ b/lib/jansson/src/hashtable.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#include "hashtable.h" +#include "jansson_private.h" /* for container_of() */ +#include /* for JSON_INLINE */ + +#ifndef INITIAL_HASHTABLE_ORDER +#define INITIAL_HASHTABLE_ORDER 3 +#endif + +typedef struct hashtable_list list_t; +typedef struct hashtable_pair pair_t; +typedef struct hashtable_bucket bucket_t; + +extern volatile uint32_t hashtable_seed; + +/* Implementation of the hash function */ +#include "lookup3.h" + +#define list_to_pair(list_) container_of(list_, pair_t, list) +#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list) +#define hash_str(key, len) ((size_t)hashlittle((key), len, hashtable_seed)) + +static JSON_INLINE void list_init(list_t *list) { + list->next = list; + list->prev = list; +} + +static JSON_INLINE void list_insert(list_t *list, list_t *node) { + node->next = list; + node->prev = list->prev; + list->prev->next = node; + list->prev = node; +} + +static JSON_INLINE void list_remove(list_t *list) { + list->prev->next = list->next; + list->next->prev = list->prev; +} + +static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) { + return bucket->first == &hashtable->list && bucket->first == bucket->last; +} + +static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, list_t *list) { + if (bucket_is_empty(hashtable, bucket)) { + list_insert(&hashtable->list, list); + bucket->first = bucket->last = list; + } else { + list_insert(bucket->first, list); + bucket->first = list; + } +} + +static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, + const char *key, size_t key_len, size_t hash) { + list_t *list; + pair_t *pair; + + if (bucket_is_empty(hashtable, bucket)) + return NULL; + + list = bucket->first; + while (1) { + pair = list_to_pair(list); + if (pair->hash == hash && pair->key_len == key_len && + memcmp(pair->key, key, key_len) == 0) + return pair; + + if (list == bucket->last) + break; + + list = list->next; + } + + return NULL; +} + +/* returns 0 on success, -1 if key was not found */ +static int hashtable_do_del(hashtable_t *hashtable, const char *key, size_t key_len, + size_t hash) { + pair_t *pair; + bucket_t *bucket; + size_t index; + + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + + pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash); + if (!pair) + return -1; + + if (&pair->list == bucket->first && &pair->list == bucket->last) + bucket->first = bucket->last = &hashtable->list; + + else if (&pair->list == bucket->first) + bucket->first = pair->list.next; + + else if (&pair->list == bucket->last) + bucket->last = pair->list.prev; + + list_remove(&pair->list); + list_remove(&pair->ordered_list); + json_decref(pair->value); + + jsonp_free(pair); + hashtable->size--; + + return 0; +} + +static void hashtable_do_clear(hashtable_t *hashtable) { + list_t *list, *next; + pair_t *pair; + + for (list = hashtable->list.next; list != &hashtable->list; list = next) { + next = list->next; + pair = list_to_pair(list); + json_decref(pair->value); + jsonp_free(pair); + } +} + +static int hashtable_do_rehash(hashtable_t *hashtable) { + list_t *list, *next; + pair_t *pair; + size_t i, index, new_size, new_order; + struct hashtable_bucket *new_buckets; + + new_order = hashtable->order + 1; + new_size = hashsize(new_order); + + new_buckets = jsonp_malloc(new_size * sizeof(bucket_t)); + if (!new_buckets) + return -1; + + jsonp_free(hashtable->buckets); + hashtable->buckets = new_buckets; + hashtable->order = new_order; + + for (i = 0; i < hashsize(hashtable->order); i++) { + hashtable->buckets[i].first = hashtable->buckets[i].last = &hashtable->list; + } + + list = hashtable->list.next; + list_init(&hashtable->list); + + for (; list != &hashtable->list; list = next) { + next = list->next; + pair = list_to_pair(list); + index = pair->hash % new_size; + insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); + } + + return 0; +} + +int hashtable_init(hashtable_t *hashtable) { + size_t i; + + hashtable->size = 0; + hashtable->order = INITIAL_HASHTABLE_ORDER; + hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t)); + if (!hashtable->buckets) + return -1; + + list_init(&hashtable->list); + list_init(&hashtable->ordered_list); + + for (i = 0; i < hashsize(hashtable->order); i++) { + hashtable->buckets[i].first = hashtable->buckets[i].last = &hashtable->list; + } + + return 0; +} + +void hashtable_close(hashtable_t *hashtable) { + hashtable_do_clear(hashtable); + jsonp_free(hashtable->buckets); +} + +static pair_t *init_pair(json_t *value, const char *key, size_t key_len, size_t hash) { + pair_t *pair; + + /* offsetof(...) returns the size of pair_t without the last, + flexible member. This way, the correct amount is + allocated. */ + + if (key_len >= (size_t)-1 - offsetof(pair_t, key)) { + /* Avoid an overflow if the key is very long */ + return NULL; + } + + pair = jsonp_malloc(offsetof(pair_t, key) + key_len + 1); + + if (!pair) + return NULL; + + pair->hash = hash; + memcpy(pair->key, key, key_len); + pair->key[key_len] = '\0'; + pair->key_len = key_len; + pair->value = value; + + list_init(&pair->list); + list_init(&pair->ordered_list); + + return pair; +} + +int hashtable_set(hashtable_t *hashtable, const char *key, size_t key_len, + json_t *value) { + pair_t *pair; + bucket_t *bucket; + size_t hash, index; + + /* rehash if the load ratio exceeds 1 */ + if (hashtable->size >= hashsize(hashtable->order)) + if (hashtable_do_rehash(hashtable)) + return -1; + + hash = hash_str(key, key_len); + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash); + + if (pair) { + json_decref(pair->value); + pair->value = value; + } else { + pair = init_pair(value, key, key_len, hash); + + if (!pair) + return -1; + + insert_to_bucket(hashtable, bucket, &pair->list); + list_insert(&hashtable->ordered_list, &pair->ordered_list); + + hashtable->size++; + } + return 0; +} + +void *hashtable_get(hashtable_t *hashtable, const char *key, size_t key_len) { + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key, key_len); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash); + if (!pair) + return NULL; + + return pair->value; +} + +int hashtable_del(hashtable_t *hashtable, const char *key, size_t key_len) { + size_t hash = hash_str(key, key_len); + return hashtable_do_del(hashtable, key, key_len, hash); +} + +void hashtable_clear(hashtable_t *hashtable) { + size_t i; + + hashtable_do_clear(hashtable); + + for (i = 0; i < hashsize(hashtable->order); i++) { + hashtable->buckets[i].first = hashtable->buckets[i].last = &hashtable->list; + } + + list_init(&hashtable->list); + list_init(&hashtable->ordered_list); + hashtable->size = 0; +} + +void *hashtable_iter(hashtable_t *hashtable) { + return hashtable_iter_next(hashtable, &hashtable->ordered_list); +} + +void *hashtable_iter_at(hashtable_t *hashtable, const char *key, size_t key_len) { + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key, key_len); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash); + if (!pair) + return NULL; + + return &pair->ordered_list; +} + +void *hashtable_iter_next(hashtable_t *hashtable, void *iter) { + list_t *list = (list_t *)iter; + if (list->next == &hashtable->ordered_list) + return NULL; + return list->next; +} + +void *hashtable_iter_key(void *iter) { + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->key; +} + +size_t hashtable_iter_key_len(void *iter) { + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->key_len; +} + +void *hashtable_iter_value(void *iter) { + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->value; +} + +void hashtable_iter_set(void *iter, json_t *value) { + pair_t *pair = ordered_list_to_pair((list_t *)iter); + + json_decref(pair->value); + pair->value = value; +} diff --git a/lib/jansson/src/hashtable.h b/lib/jansson/src/hashtable.h new file mode 100644 index 0000000..03a1f5a --- /dev/null +++ b/lib/jansson/src/hashtable.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef HASHTABLE_H +#define HASHTABLE_H + +#include "jansson.h" +#include + +struct hashtable_list { + struct hashtable_list *prev; + struct hashtable_list *next; +}; + +/* "pair" may be a bit confusing a name, but think of it as a + key-value pair. In this case, it just encodes some extra data, + too */ +struct hashtable_pair { + struct hashtable_list list; + struct hashtable_list ordered_list; + size_t hash; + json_t *value; + size_t key_len; + char key[1]; +}; + +struct hashtable_bucket { + struct hashtable_list *first; + struct hashtable_list *last; +}; + +typedef struct hashtable { + size_t size; + struct hashtable_bucket *buckets; + size_t order; /* hashtable has pow(2, order) buckets */ + struct hashtable_list list; + struct hashtable_list ordered_list; +} hashtable_t; + +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->ordered_list)) + +/** + * hashtable_init - Initialize a hashtable object + * + * @hashtable: The (statically allocated) hashtable object + * + * Initializes a statically allocated hashtable object. The object + * should be cleared with hashtable_close when it's no longer used. + * + * Returns 0 on success, -1 on error (out of memory). + */ +int hashtable_init(hashtable_t *hashtable) JANSSON_ATTRS((warn_unused_result)); + +/** + * hashtable_close - Release all resources used by a hashtable object + * + * @hashtable: The hashtable + * + * Destroys a statically allocated hashtable object. + */ +void hashtable_close(hashtable_t *hashtable); + +/** + * hashtable_set - Add/modify value in hashtable + * + * @hashtable: The hashtable object + * @key: The key + * @key: The length of key + * @serial: For addition order of keys + * @value: The value + * + * If a value with the given key already exists, its value is replaced + * with the new value. Value is "stealed" in the sense that hashtable + * doesn't increment its refcount but decreases the refcount when the + * value is no longer needed. + * + * Returns 0 on success, -1 on failure (out of memory). + */ +int hashtable_set(hashtable_t *hashtable, const char *key, size_t key_len, json_t *value); + +/** + * hashtable_get - Get a value associated with a key + * + * @hashtable: The hashtable object + * @key: The key + * @key: The length of key + * + * Returns value if it is found, or NULL otherwise. + */ +void *hashtable_get(hashtable_t *hashtable, const char *key, size_t key_len); + +/** + * hashtable_del - Remove a value from the hashtable + * + * @hashtable: The hashtable object + * @key: The key + * @key: The length of key + * + * Returns 0 on success, or -1 if the key was not found. + */ +int hashtable_del(hashtable_t *hashtable, const char *key, size_t key_len); + +/** + * hashtable_clear - Clear hashtable + * + * @hashtable: The hashtable object + * + * Removes all items from the hashtable. + */ +void hashtable_clear(hashtable_t *hashtable); + +/** + * hashtable_iter - Iterate over hashtable + * + * @hashtable: The hashtable object + * + * Returns an opaque iterator to the first element in the hashtable. + * The iterator should be passed to hashtable_iter_* functions. + * The hashtable items are not iterated over in any particular order. + * + * There's no need to free the iterator in any way. The iterator is + * valid as long as the item that is referenced by the iterator is not + * deleted. Other values may be added or deleted. In particular, + * hashtable_iter_next() may be called on an iterator, and after that + * the key/value pair pointed by the old iterator may be deleted. + */ +void *hashtable_iter(hashtable_t *hashtable); + +/** + * hashtable_iter_at - Return an iterator at a specific key + * + * @hashtable: The hashtable object + * @key: The key that the iterator should point to + * @key: The length of key + * + * Like hashtable_iter() but returns an iterator pointing to a + * specific key. + */ +void *hashtable_iter_at(hashtable_t *hashtable, const char *key, size_t key_len); + +/** + * hashtable_iter_next - Advance an iterator + * + * @hashtable: The hashtable object + * @iter: The iterator + * + * Returns a new iterator pointing to the next element in the + * hashtable or NULL if the whole hastable has been iterated over. + */ +void *hashtable_iter_next(hashtable_t *hashtable, void *iter); + +/** + * hashtable_iter_key - Retrieve the key pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_key(void *iter); + +/** + * hashtable_iter_key_len - Retrieve the key length pointed by an iterator + * + * @iter: The iterator + */ +size_t hashtable_iter_key_len(void *iter); + +/** + * hashtable_iter_value - Retrieve the value pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_value(void *iter); + +/** + * hashtable_iter_set - Set the value pointed by an iterator + * + * @iter: The iterator + * @value: The value to set + */ +void hashtable_iter_set(void *iter, json_t *value); + +#endif diff --git a/lib/jansson/src/hashtable_seed.c b/lib/jansson/src/hashtable_seed.c new file mode 100644 index 0000000..d156b40 --- /dev/null +++ b/lib/jansson/src/hashtable_seed.c @@ -0,0 +1,277 @@ +/* Generate sizeof(uint32_t) bytes of as random data as possible to seed + the hash function. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined(_WIN32) +/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */ +#include +#endif + +#include "jansson.h" + +static uint32_t buf_to_uint32(char *data) { + size_t i; + uint32_t result = 0; + + for (i = 0; i < sizeof(uint32_t); i++) + result = (result << 8) | (unsigned char)data[i]; + + return result; +} + +/* /dev/urandom */ +#if !defined(_WIN32) && defined(USE_URANDOM) +static int seed_from_urandom(uint32_t *seed) { + /* Use unbuffered I/O if we have open(), close() and read(). Otherwise + fall back to fopen() */ + + char data[sizeof(uint32_t)]; + int ok; + +#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ) + int urandom; + urandom = open("/dev/urandom", O_RDONLY); + if (urandom == -1) + return 1; + + ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t); + close(urandom); +#else + FILE *urandom; + + urandom = fopen("/dev/urandom", "rb"); + if (!urandom) + return 1; + + ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t); + fclose(urandom); +#endif + + if (!ok) + return 1; + + *seed = buf_to_uint32(data); + return 0; +} +#endif + +/* Windows Crypto API */ +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) +#include + +typedef BOOL(WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, + LPCSTR pszProvider, DWORD dwProvType, + DWORD dwFlags); +typedef BOOL(WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer); +typedef BOOL(WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); + +static int seed_from_windows_cryptoapi(uint32_t *seed) { + HINSTANCE hAdvAPI32 = NULL; + CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; + CRYPTGENRANDOM pCryptGenRandom = NULL; + CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; + HCRYPTPROV hCryptProv = 0; + BYTE data[sizeof(uint32_t)]; + int ok; + + hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll")); + if (hAdvAPI32 == NULL) + return 1; + + pCryptAcquireContext = + (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); + if (!pCryptAcquireContext) + return 1; + + pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom"); + if (!pCryptGenRandom) + return 1; + + pCryptReleaseContext = + (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext"); + if (!pCryptReleaseContext) + return 1; + + if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return 1; + + ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data); + pCryptReleaseContext(hCryptProv, 0); + + if (!ok) + return 1; + + *seed = buf_to_uint32((char *)data); + return 0; +} +#endif + +/* gettimeofday() and getpid() */ +static int seed_from_timestamp_and_pid(uint32_t *seed) { +#ifdef HAVE_GETTIMEOFDAY + /* XOR of seconds and microseconds */ + struct timeval tv; + gettimeofday(&tv, NULL); + *seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec; +#else + /* Seconds only */ + *seed = (uint32_t)time(NULL); +#endif + + /* XOR with PID for more randomness */ +#if defined(_WIN32) + *seed ^= (uint32_t)GetCurrentProcessId(); +#elif defined(HAVE_GETPID) + *seed ^= (uint32_t)getpid(); +#endif + + return 0; +} + +static uint32_t generate_seed() { + uint32_t seed = 0; + int done = 0; + +#if !defined(_WIN32) && defined(USE_URANDOM) + if (seed_from_urandom(&seed) == 0) + done = 1; +#endif + +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) + if (seed_from_windows_cryptoapi(&seed) == 0) + done = 1; +#endif + + if (!done) { + /* Fall back to timestamp and PID if no better randomness is + available */ + seed_from_timestamp_and_pid(&seed); + } + + /* Make sure the seed is never zero */ + if (seed == 0) + seed = 1; + + return seed; +} + +volatile uint32_t hashtable_seed = 0; + +#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +static volatile char seed_initialized = 0; + +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); + } else { + /* Wait for another thread to do the seeding */ + do { +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } while (__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); + } + } +} +#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) { + /* Explicit synchronization fences are not supported by the + __sync builtins, so every thread getting here has to + generate the seed value. + */ + new_seed = generate_seed(); + } + + do { + if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) { + /* We were the first to seed */ + break; + } else { + /* Wait for another thread to do the seeding */ +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } + } while (hashtable_seed == 0); + } +} +#elif defined(_WIN32) +static long seed_initialized = 0; +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (InterlockedIncrement(&seed_initialized) == 1) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } else { + /* Wait for another thread to do the seeding */ + do { + SwitchToThread(); + } while (hashtable_seed == 0); + } + } +} +#else +/* Fall back to a thread-unsafe version */ +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } +} +#endif diff --git a/lib/jansson/src/jansson.def b/lib/jansson/src/jansson.def new file mode 100644 index 0000000..5c76c2f --- /dev/null +++ b/lib/jansson/src/jansson.def @@ -0,0 +1,83 @@ +EXPORTS + json_delete + json_true + json_false + json_null + json_sprintf + json_vsprintf + json_string + json_stringn + json_string_nocheck + json_stringn_nocheck + json_string_value + json_string_length + json_string_set + json_string_setn + json_string_set_nocheck + json_string_setn_nocheck + json_integer + json_integer_value + json_integer_set + json_real + json_real_value + json_real_set + json_number_value + json_array + json_array_size + json_array_get + json_array_set_new + json_array_append_new + json_array_insert_new + json_array_remove + json_array_clear + json_array_extend + json_object + json_object_size + json_object_get + json_object_getn + json_object_set_new + json_object_setn_new + json_object_set_new_nocheck + json_object_setn_new_nocheck + json_object_del + json_object_deln + json_object_clear + json_object_update + json_object_update_existing + json_object_update_missing + json_object_update_recursive + json_object_iter + json_object_iter_at + json_object_iter_next + json_object_iter_key + json_object_iter_key_len + json_object_iter_value + json_object_iter_set_new + json_object_key_to_iter + json_object_seed + json_dumps + json_dumpb + json_dumpf + json_dumpfd + json_dump_file + json_dump_callback + json_loads + json_loadb + json_loadf + json_loadfd + json_load_file + json_load_callback + json_equal + json_copy + json_deep_copy + json_pack + json_pack_ex + json_vpack_ex + json_unpack + json_unpack_ex + json_vunpack_ex + json_set_alloc_funcs + json_get_alloc_funcs + jansson_version_str + jansson_version_cmp + diff --git a/lib/jansson/src/jansson.h b/lib/jansson/src/jansson.h new file mode 100644 index 0000000..391c85e --- /dev/null +++ b/lib/jansson/src/jansson.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_H +#define JANSSON_H + +#include +#include +#include /* for size_t */ + +#include "jansson_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ + +#define JANSSON_MAJOR_VERSION 2 +#define JANSSON_MINOR_VERSION 14 +#define JANSSON_MICRO_VERSION 0 + +/* Micro version is omitted if it's 0 */ +#define JANSSON_VERSION "2.14" + +/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this + for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ +#define JANSSON_VERSION_HEX \ + ((JANSSON_MAJOR_VERSION << 16) | (JANSSON_MINOR_VERSION << 8) | \ + (JANSSON_MICRO_VERSION << 0)) + +/* If __atomic or __sync builtins are available the library is thread + * safe for all read-only functions plus reference counting. */ +#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS +#define JANSSON_THREAD_SAFE_REFCOUNT 1 +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define JANSSON_ATTRS(x) __attribute__(x) +#else +#define JANSSON_ATTRS(x) +#endif + +/* types */ + +typedef enum { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} json_type; + +typedef struct json_t { + json_type type; + volatile size_t refcount; +} json_t; + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else +#define JSON_INTEGER_FORMAT "lld" +#endif +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif + +#define json_typeof(json) ((json)->type) +#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT) +#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY) +#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) +#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) +#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) +#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) +#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) +#define json_boolean_value json_is_true +#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) +#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL) + +/* construction, destruction, reference counting */ + +json_t *json_object(void); +json_t *json_array(void); +json_t *json_string(const char *value); +json_t *json_stringn(const char *value, size_t len); +json_t *json_string_nocheck(const char *value); +json_t *json_stringn_nocheck(const char *value, size_t len); +json_t *json_integer(json_int_t value); +json_t *json_real(double value); +json_t *json_true(void); +json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) +json_t *json_null(void); + +/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */ +#if JSON_HAVE_ATOMIC_BUILTINS +#define JSON_INTERNAL_INCREF(json) \ + __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE) +#define JSON_INTERNAL_DECREF(json) \ + __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE) +#elif JSON_HAVE_SYNC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1) +#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1) +#else +#define JSON_INTERNAL_INCREF(json) (++json->refcount) +#define JSON_INTERNAL_DECREF(json) (--json->refcount) +#endif + +static JSON_INLINE json_t *json_incref(json_t *json) { + if (json && json->refcount != (size_t)-1) + JSON_INTERNAL_INCREF(json); + return json; +} + +/* do not call json_delete directly */ +void json_delete(json_t *json); + +static JSON_INLINE void json_decref(json_t *json) { + if (json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0) + json_delete(json); +} + +#if defined(__GNUC__) || defined(__clang__) +static JSON_INLINE void json_decrefp(json_t **json) { + if (json) { + json_decref(*json); + *json = NULL; + } +} + +#define json_auto_t json_t __attribute__((cleanup(json_decrefp))) +#endif + +/* error reporting */ + +#define JSON_ERROR_TEXT_LENGTH 160 +#define JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct json_error_t { + int line; + int column; + int position; + char source[JSON_ERROR_SOURCE_LENGTH]; + char text[JSON_ERROR_TEXT_LENGTH]; +} json_error_t; + +enum json_error_code { + json_error_unknown, + json_error_out_of_memory, + json_error_stack_overflow, + json_error_cannot_open_file, + json_error_invalid_argument, + json_error_invalid_utf8, + json_error_premature_end_of_input, + json_error_end_of_input_expected, + json_error_invalid_syntax, + json_error_invalid_format, + json_error_wrong_type, + json_error_null_character, + json_error_null_value, + json_error_null_byte_in_key, + json_error_duplicate_key, + json_error_numeric_overflow, + json_error_item_not_found, + json_error_index_out_of_range +}; + +static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) { + return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1]; +} + +/* getters, setters, manipulation */ + +void json_object_seed(size_t seed); +size_t json_object_size(const json_t *object); +json_t *json_object_get(const json_t *object, const char *key) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_object_getn(const json_t *object, const char *key, size_t key_len) + JANSSON_ATTRS((warn_unused_result)); +int json_object_set_new(json_t *object, const char *key, json_t *value); +int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value); +int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); +int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len, + json_t *value); +int json_object_del(json_t *object, const char *key); +int json_object_deln(json_t *object, const char *key, size_t key_len); +int json_object_clear(json_t *object); +int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); +int json_object_update_recursive(json_t *object, json_t *other); +void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); +void *json_object_iter_next(json_t *object, void *iter); +const char *json_object_iter_key(void *iter); +size_t json_object_iter_key_len(void *iter); +json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); + +#define json_object_foreach(object, key, value) \ + for (key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key( \ + json_object_iter_next(object, json_object_key_to_iter(key)))) + +#define json_object_keylen_foreach(object, key, key_len, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key( \ + json_object_iter_next(object, json_object_key_to_iter(key))), \ + key_len = json_object_iter_key_len(json_object_key_to_iter(key))) + +#define json_object_foreach_safe(object, n, key, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) + +#define json_object_keylen_foreach_safe(object, n, key, key_len, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)), \ + key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), key_len = json_object_iter_key_len(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) + +#define json_array_foreach(array, index, value) \ + for (index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) + +static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) { + return json_object_set_new(object, key, json_incref(value)); +} + +static JSON_INLINE int json_object_setn(json_t *object, const char *key, size_t key_len, + json_t *value) { + return json_object_setn_new(object, key, key_len, json_incref(value)); +} + +static JSON_INLINE int json_object_set_nocheck(json_t *object, const char *key, + json_t *value) { + return json_object_set_new_nocheck(object, key, json_incref(value)); +} + +static JSON_INLINE int json_object_setn_nocheck(json_t *object, const char *key, + size_t key_len, json_t *value) { + return json_object_setn_new_nocheck(object, key, key_len, json_incref(value)); +} + +static JSON_INLINE int json_object_iter_set(json_t *object, void *iter, json_t *value) { + return json_object_iter_set_new(object, iter, json_incref(value)); +} + +static JSON_INLINE int json_object_update_new(json_t *object, json_t *other) { + int ret = json_object_update(object, other); + json_decref(other); + return ret; +} + +static JSON_INLINE int json_object_update_existing_new(json_t *object, json_t *other) { + int ret = json_object_update_existing(object, other); + json_decref(other); + return ret; +} + +static JSON_INLINE int json_object_update_missing_new(json_t *object, json_t *other) { + int ret = json_object_update_missing(object, other); + json_decref(other); + return ret; +} + +size_t json_array_size(const json_t *array); +json_t *json_array_get(const json_t *array, size_t index) + JANSSON_ATTRS((warn_unused_result)); +int json_array_set_new(json_t *array, size_t index, json_t *value); +int json_array_append_new(json_t *array, json_t *value); +int json_array_insert_new(json_t *array, size_t index, json_t *value); +int json_array_remove(json_t *array, size_t index); +int json_array_clear(json_t *array); +int json_array_extend(json_t *array, json_t *other); + +static JSON_INLINE int json_array_set(json_t *array, size_t ind, json_t *value) { + return json_array_set_new(array, ind, json_incref(value)); +} + +static JSON_INLINE int json_array_append(json_t *array, json_t *value) { + return json_array_append_new(array, json_incref(value)); +} + +static JSON_INLINE int json_array_insert(json_t *array, size_t ind, json_t *value) { + return json_array_insert_new(array, ind, json_incref(value)); +} + +const char *json_string_value(const json_t *string); +size_t json_string_length(const json_t *string); +json_int_t json_integer_value(const json_t *integer); +double json_real_value(const json_t *real); +double json_number_value(const json_t *json); + +int json_string_set(json_t *string, const char *value); +int json_string_setn(json_t *string, const char *value, size_t len); +int json_string_set_nocheck(json_t *string, const char *value); +int json_string_setn_nocheck(json_t *string, const char *value, size_t len); +int json_integer_set(json_t *integer, json_int_t value); +int json_real_set(json_t *real, double value); + +/* pack, unpack */ + +json_t *json_pack(const char *fmt, ...) JANSSON_ATTRS((warn_unused_result)); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) + JANSSON_ATTRS((warn_unused_result)); + +#define JSON_VALIDATE_ONLY 0x1 +#define JSON_STRICT 0x2 + +int json_unpack(json_t *root, const char *fmt, ...); +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, + va_list ap); + +/* sprintf */ + +json_t *json_sprintf(const char *fmt, ...) + JANSSON_ATTRS((warn_unused_result, format(printf, 1, 2))); +json_t *json_vsprintf(const char *fmt, va_list ap) + JANSSON_ATTRS((warn_unused_result, format(printf, 1, 0))); + +/* equality */ + +int json_equal(const json_t *value1, const json_t *value2); + +/* copying */ + +json_t *json_copy(json_t *value) JANSSON_ATTRS((warn_unused_result)); +json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS((warn_unused_result)); + +/* decoding */ + +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 +#define JSON_ALLOW_NUL 0x10 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + +json_t *json_loads(const char *input, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadfd(int input, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, + json_error_t *error) JANSSON_ATTRS((warn_unused_result)); + +/* encoding */ + +#define JSON_MAX_INDENT 0x1F +#define JSON_INDENT(n) ((n)&JSON_MAX_INDENT) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 +#define JSON_REAL_PRECISION(n) (((n)&0x1F) << 11) +#define JSON_EMBED 0x10000 + +typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); + +char *json_dumps(const json_t *json, size_t flags) JANSSON_ATTRS((warn_unused_result)); +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); +int json_dumpf(const json_t *json, FILE *output, size_t flags); +int json_dumpfd(const json_t *json, int output, size_t flags); +int json_dump_file(const json_t *json, const char *path, size_t flags); +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, + size_t flags); + +/* custom memory allocation */ + +typedef void *(*json_malloc_t)(size_t); +typedef void (*json_free_t)(void *); + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn); + +/* runtime version checking */ + +const char *jansson_version_str(void); +int jansson_version_cmp(int major, int minor, int micro); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/jansson/src/jansson_config.h.in b/lib/jansson/src/jansson_config.h.in new file mode 100644 index 0000000..791f60d --- /dev/null +++ b/lib/jansson/src/jansson_config.h.in @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @json_inline@ +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@ + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@ + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@ + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/lib/jansson/src/jansson_private.h b/lib/jansson/src/jansson_private.h new file mode 100644 index 0000000..ea2593c --- /dev/null +++ b/lib/jansson/src/jansson_private.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_PRIVATE_H +#define JANSSON_PRIVATE_H + +#include "hashtable.h" +#include "jansson.h" +#include "jansson_private_config.h" +#include "strbuffer.h" +#include + +#define container_of(ptr_, type_, member_) \ + ((type_ *)((char *)ptr_ - offsetof(type_, member_))) + +/* On some platforms, max() may already be defined */ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* va_copy is a C99 feature. In C89 implementations, it's sometimes + available as __va_copy. If not, memcpy() should do the trick. */ +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) +#endif +#endif + +typedef struct { + json_t json; + hashtable_t hashtable; +} json_object_t; + +typedef struct { + json_t json; + size_t size; + size_t entries; + json_t **table; +} json_array_t; + +typedef struct { + json_t json; + char *value; + size_t length; +} json_string_t; + +typedef struct { + json_t json; + double value; +} json_real_t; + +typedef struct { + json_t json; + json_int_t value; +} json_integer_t; + +#define json_to_object(json_) container_of(json_, json_object_t, json) +#define json_to_array(json_) container_of(json_, json_array_t, json) +#define json_to_string(json_) container_of(json_, json_string_t, json) +#define json_to_real(json_) container_of(json_, json_real_t, json) +#define json_to_integer(json_) container_of(json_, json_integer_t, json) + +/* Create a string by taking ownership of an existing buffer */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); + +/* Error message formatting */ +void jsonp_error_init(json_error_t *error, const char *source); +void jsonp_error_set_source(json_error_t *error, const char *source); +void jsonp_error_set(json_error_t *error, int line, int column, size_t position, + enum json_error_code code, const char *msg, ...); +void jsonp_error_vset(json_error_t *error, int line, int column, size_t position, + enum json_error_code code, const char *msg, va_list ap); + +/* Locale independent string<->double conversions */ +int jsonp_strtod(strbuffer_t *strbuffer, double *out); +int jsonp_dtostr(char *buffer, size_t size, double value, int prec); + +/* Wrappers for custom memory functions */ +void *jsonp_malloc(size_t size) JANSSON_ATTRS((warn_unused_result)); +void jsonp_free(void *ptr); +char *jsonp_strndup(const char *str, size_t length) JANSSON_ATTRS((warn_unused_result)); +char *jsonp_strdup(const char *str) JANSSON_ATTRS((warn_unused_result)); +char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS((warn_unused_result)); + +/* Circular reference check*/ +/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */ +#define LOOP_KEY_LEN (2 + (sizeof(json_t *) * 2) + 1) +int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size, + size_t *key_len_out); + +/* Windows compatibility */ +#if defined(_WIN32) || defined(WIN32) +#if defined(_MSC_VER) /* MS compiller */ +#if (_MSC_VER < 1900) && \ + !defined(snprintf) /* snprintf not defined yet & not introduced */ +#define snprintf _snprintf +#endif +#if (_MSC_VER < 1500) && \ + !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */ +#define vsnprintf(b, c, f, a) _vsnprintf(b, c, f, a) +#endif +#else /* Other Windows compiller, old definition */ +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif +#endif + +#endif diff --git a/lib/jansson/src/load.c b/lib/jansson/src/load.c new file mode 100644 index 0000000..8ae7abd --- /dev/null +++ b/lib/jansson/src/load.c @@ -0,0 +1,1106 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson_private.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "jansson.h" +#include "strbuffer.h" +#include "utf.h" + +#define STREAM_STATE_OK 0 +#define STREAM_STATE_EOF -1 +#define STREAM_STATE_ERROR -2 + +#define TOKEN_INVALID -1 +#define TOKEN_EOF 0 +#define TOKEN_STRING 256 +#define TOKEN_INTEGER 257 +#define TOKEN_REAL 258 +#define TOKEN_TRUE 259 +#define TOKEN_FALSE 260 +#define TOKEN_NULL 261 + +/* Locale independent versions of isxxx() functions */ +#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') +#define l_islower(c) ('a' <= (c) && (c) <= 'z') +#define l_isalpha(c) (l_isupper(c) || l_islower(c)) +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isxdigit(c) \ + (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) + +/* Read one byte from stream, convert to unsigned char, then int, and + return. return EOF on end of file. This corresponds to the + behaviour of fgetc(). */ +typedef int (*get_func)(void *data); + +typedef struct { + get_func get; + void *data; + char buffer[5]; + size_t buffer_pos; + int state; + int line; + int column, last_column; + size_t position; +} stream_t; + +typedef struct { + stream_t stream; + strbuffer_t saved_text; + size_t flags; + size_t depth; + int token; + union { + struct { + char *val; + size_t len; + } string; + json_int_t integer; + double real; + } value; +} lex_t; + +#define stream_to_lex(stream) container_of(stream, lex_t, stream) + +/*** error reporting ***/ + +static void error_set(json_error_t *error, const lex_t *lex, enum json_error_code code, + const char *msg, ...) { + va_list ap; + char msg_text[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH]; + + int line = -1, col = -1; + size_t pos = 0; + const char *result = msg_text; + + if (!error) + return; + + va_start(ap, msg); + vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); + msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + va_end(ap); + + if (lex) { + const char *saved_text = strbuffer_value(&lex->saved_text); + + line = lex->stream.line; + col = lex->stream.column; + pos = lex->stream.position; + + if (saved_text && saved_text[0]) { + if (lex->saved_text.length <= 20) { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, "%s near '%s'", + msg_text, saved_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } else { + if (code == json_error_invalid_syntax) { + /* More specific error code for premature end of file. */ + code = json_error_premature_end_of_input; + } + if (lex->stream.state == STREAM_STATE_ERROR) { + /* No context for UTF-8 decoding errors */ + result = msg_text; + } else { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, "%s near end of file", + msg_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + } + + jsonp_error_set(error, line, col, pos, code, "%s", result); +} + +/*** lexical analyzer ***/ + +static void stream_init(stream_t *stream, get_func get, void *data) { + stream->get = get; + stream->data = data; + stream->buffer[0] = '\0'; + stream->buffer_pos = 0; + + stream->state = STREAM_STATE_OK; + stream->line = 1; + stream->column = 0; + stream->position = 0; +} + +static int stream_get(stream_t *stream, json_error_t *error) { + int c; + + if (stream->state != STREAM_STATE_OK) + return stream->state; + + if (!stream->buffer[stream->buffer_pos]) { + c = stream->get(stream->data); + if (c == EOF) { + stream->state = STREAM_STATE_EOF; + return STREAM_STATE_EOF; + } + + stream->buffer[0] = c; + stream->buffer_pos = 0; + + if (0x80 <= c && c <= 0xFF) { + /* multi-byte UTF-8 sequence */ + size_t i, count; + + count = utf8_check_first(c); + if (!count) + goto out; + + assert(count >= 2); + + for (i = 1; i < count; i++) + stream->buffer[i] = stream->get(stream->data); + + if (!utf8_check_full(stream->buffer, count, NULL)) + goto out; + + stream->buffer[count] = '\0'; + } else + stream->buffer[1] = '\0'; + } + + c = stream->buffer[stream->buffer_pos++]; + + stream->position++; + if (c == '\n') { + stream->line++; + stream->last_column = stream->column; + stream->column = 0; + } else if (utf8_check_first(c)) { + /* track the Unicode character column, so increment only if + this is the first character of a UTF-8 sequence */ + stream->column++; + } + + return c; + +out: + stream->state = STREAM_STATE_ERROR; + error_set(error, stream_to_lex(stream), json_error_invalid_utf8, + "unable to decode byte 0x%x", c); + return STREAM_STATE_ERROR; +} + +static void stream_unget(stream_t *stream, int c) { + if (c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR) + return; + + stream->position--; + if (c == '\n') { + stream->line--; + stream->column = stream->last_column; + } else if (utf8_check_first(c)) + stream->column--; + + assert(stream->buffer_pos > 0); + stream->buffer_pos--; + assert(stream->buffer[stream->buffer_pos] == c); +} + +static int lex_get(lex_t *lex, json_error_t *error) { + return stream_get(&lex->stream, error); +} + +static void lex_save(lex_t *lex, int c) { strbuffer_append_byte(&lex->saved_text, c); } + +static int lex_get_save(lex_t *lex, json_error_t *error) { + int c = stream_get(&lex->stream, error); + if (c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) + lex_save(lex, c); + return c; +} + +static void lex_unget(lex_t *lex, int c) { stream_unget(&lex->stream, c); } + +static void lex_unget_unsave(lex_t *lex, int c) { + if (c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { +/* Since we treat warnings as errors, when assertions are turned + * off the "d" variable would be set but never used. Which is + * treated as an error by GCC. + */ +#ifndef NDEBUG + char d; +#endif + stream_unget(&lex->stream, c); +#ifndef NDEBUG + d = +#endif + strbuffer_pop(&lex->saved_text); + assert(c == d); + } +} + +static void lex_save_cached(lex_t *lex) { + while (lex->stream.buffer[lex->stream.buffer_pos] != '\0') { + lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]); + lex->stream.buffer_pos++; + lex->stream.position++; + } +} + +static void lex_free_string(lex_t *lex) { + jsonp_free(lex->value.string.val); + lex->value.string.val = NULL; + lex->value.string.len = 0; +} + +/* assumes that str points to 'u' plus at least 4 valid hex digits */ +static int32_t decode_unicode_escape(const char *str) { + int i; + int32_t value = 0; + + assert(str[0] == 'u'); + + for (i = 1; i <= 4; i++) { + char c = str[i]; + value <<= 4; + if (l_isdigit(c)) + value += c - '0'; + else if (l_islower(c)) + value += c - 'a' + 10; + else if (l_isupper(c)) + value += c - 'A' + 10; + else + return -1; + } + + return value; +} + +static void lex_scan_string(lex_t *lex, json_error_t *error) { + int c; + const char *p; + char *t; + int i; + + lex->value.string.val = NULL; + lex->token = TOKEN_INVALID; + + c = lex_get_save(lex, error); + + while (c != '"') { + if (c == STREAM_STATE_ERROR) + goto out; + + else if (c == STREAM_STATE_EOF) { + error_set(error, lex, json_error_premature_end_of_input, + "premature end of input"); + goto out; + } + + else if (0 <= c && c <= 0x1F) { + /* control character */ + lex_unget_unsave(lex, c); + if (c == '\n') + error_set(error, lex, json_error_invalid_syntax, "unexpected newline"); + else + error_set(error, lex, json_error_invalid_syntax, "control character 0x%x", + c); + goto out; + } + + else if (c == '\\') { + c = lex_get_save(lex, error); + if (c == 'u') { + c = lex_get_save(lex, error); + for (i = 0; i < 4; i++) { + if (!l_isxdigit(c)) { + error_set(error, lex, json_error_invalid_syntax, + "invalid escape"); + goto out; + } + c = lex_get_save(lex, error); + } + } else if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || + c == 'n' || c == 'r' || c == 't') + c = lex_get_save(lex, error); + else { + error_set(error, lex, json_error_invalid_syntax, "invalid escape"); + goto out; + } + } else + c = lex_get_save(lex, error); + } + + /* the actual value is at most of the same length as the source + string, because: + - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte + - a single \uXXXX escape (length 6) is converted to at most 3 bytes + - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair + are converted to 4 bytes + */ + t = jsonp_malloc(lex->saved_text.length + 1); + if (!t) { + /* this is not very nice, since TOKEN_INVALID is returned */ + goto out; + } + lex->value.string.val = t; + + /* + 1 to skip the " */ + p = strbuffer_value(&lex->saved_text) + 1; + + while (*p != '"') { + if (*p == '\\') { + p++; + if (*p == 'u') { + size_t length; + int32_t value; + + value = decode_unicode_escape(p); + if (value < 0) { + error_set(error, lex, json_error_invalid_syntax, + "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if (0xD800 <= value && value <= 0xDBFF) { + /* surrogate pair */ + if (*p == '\\' && *(p + 1) == 'u') { + int32_t value2 = decode_unicode_escape(++p); + if (value2 < 0) { + error_set(error, lex, json_error_invalid_syntax, + "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if (0xDC00 <= value2 && value2 <= 0xDFFF) { + /* valid second surrogate */ + value = + ((value - 0xD800) << 10) + (value2 - 0xDC00) + 0x10000; + } else { + /* invalid second surrogate */ + error_set(error, lex, json_error_invalid_syntax, + "invalid Unicode '\\u%04X\\u%04X'", value, value2); + goto out; + } + } else { + /* no second surrogate */ + error_set(error, lex, json_error_invalid_syntax, + "invalid Unicode '\\u%04X'", value); + goto out; + } + } else if (0xDC00 <= value && value <= 0xDFFF) { + error_set(error, lex, json_error_invalid_syntax, + "invalid Unicode '\\u%04X'", value); + goto out; + } + + if (utf8_encode(value, t, &length)) + assert(0); + t += length; + } else { + switch (*p) { + case '"': + case '\\': + case '/': + *t = *p; + break; + case 'b': + *t = '\b'; + break; + case 'f': + *t = '\f'; + break; + case 'n': + *t = '\n'; + break; + case 'r': + *t = '\r'; + break; + case 't': + *t = '\t'; + break; + default: + assert(0); + } + t++; + p++; + } + } else + *(t++) = *(p++); + } + *t = '\0'; + lex->value.string.len = t - lex->value.string.val; + lex->token = TOKEN_STRING; + return; + +out: + lex_free_string(lex); +} + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _MSC_VER /* Microsoft Visual Studio */ +#define json_strtoint _strtoi64 +#else +#define json_strtoint strtoll +#endif +#else +#define json_strtoint strtol +#endif +#endif + +static int lex_scan_number(lex_t *lex, int c, json_error_t *error) { + const char *saved_text; + char *end; + double doubleval; + + lex->token = TOKEN_INVALID; + + if (c == '-') + c = lex_get_save(lex, error); + + if (c == '0') { + c = lex_get_save(lex, error); + if (l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + } else if (l_isdigit(c)) { + do + c = lex_get_save(lex, error); + while (l_isdigit(c)); + } else { + lex_unget_unsave(lex, c); + goto out; + } + + if (!(lex->flags & JSON_DECODE_INT_AS_REAL) && c != '.' && c != 'E' && c != 'e') { + json_int_t intval; + + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + errno = 0; + intval = json_strtoint(saved_text, &end, 10); + if (errno == ERANGE) { + if (intval < 0) + error_set(error, lex, json_error_numeric_overflow, + "too big negative integer"); + else + error_set(error, lex, json_error_numeric_overflow, "too big integer"); + goto out; + } + + assert(end == saved_text + lex->saved_text.length); + + lex->token = TOKEN_INTEGER; + lex->value.integer = intval; + return 0; + } + + if (c == '.') { + c = lex_get(lex, error); + if (!l_isdigit(c)) { + lex_unget(lex, c); + goto out; + } + lex_save(lex, c); + + do + c = lex_get_save(lex, error); + while (l_isdigit(c)); + } + + if (c == 'E' || c == 'e') { + c = lex_get_save(lex, error); + if (c == '+' || c == '-') + c = lex_get_save(lex, error); + + if (!l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + + do + c = lex_get_save(lex, error); + while (l_isdigit(c)); + } + + lex_unget_unsave(lex, c); + + if (jsonp_strtod(&lex->saved_text, &doubleval)) { + error_set(error, lex, json_error_numeric_overflow, "real number overflow"); + goto out; + } + + lex->token = TOKEN_REAL; + lex->value.real = doubleval; + return 0; + +out: + return -1; +} + +static int lex_scan(lex_t *lex, json_error_t *error) { + int c; + + strbuffer_clear(&lex->saved_text); + + if (lex->token == TOKEN_STRING) + lex_free_string(lex); + + do + c = lex_get(lex, error); + while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); + + if (c == STREAM_STATE_EOF) { + lex->token = TOKEN_EOF; + goto out; + } + + if (c == STREAM_STATE_ERROR) { + lex->token = TOKEN_INVALID; + goto out; + } + + lex_save(lex, c); + + if (c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') + lex->token = c; + + else if (c == '"') + lex_scan_string(lex, error); + + else if (l_isdigit(c) || c == '-') { + if (lex_scan_number(lex, c, error)) + goto out; + } + + else if (l_isalpha(c)) { + /* eat up the whole identifier for clearer error messages */ + const char *saved_text; + + do + c = lex_get_save(lex, error); + while (l_isalpha(c)); + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + if (strcmp(saved_text, "true") == 0) + lex->token = TOKEN_TRUE; + else if (strcmp(saved_text, "false") == 0) + lex->token = TOKEN_FALSE; + else if (strcmp(saved_text, "null") == 0) + lex->token = TOKEN_NULL; + else + lex->token = TOKEN_INVALID; + } + + else { + /* save the rest of the input UTF-8 sequence to get an error + message of valid UTF-8 */ + lex_save_cached(lex); + lex->token = TOKEN_INVALID; + } + +out: + return lex->token; +} + +static char *lex_steal_string(lex_t *lex, size_t *out_len) { + char *result = NULL; + if (lex->token == TOKEN_STRING) { + result = lex->value.string.val; + *out_len = lex->value.string.len; + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + return result; +} + +static int lex_init(lex_t *lex, get_func get, size_t flags, void *data) { + stream_init(&lex->stream, get, data); + if (strbuffer_init(&lex->saved_text)) + return -1; + + lex->flags = flags; + lex->token = TOKEN_INVALID; + return 0; +} + +static void lex_close(lex_t *lex) { + if (lex->token == TOKEN_STRING) + lex_free_string(lex); + strbuffer_close(&lex->saved_text); +} + +/*** parser ***/ + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error); + +static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) { + json_t *object = json_object(); + if (!object) + return NULL; + + lex_scan(lex, error); + if (lex->token == '}') + return object; + + while (1) { + char *key; + size_t len; + json_t *value; + + if (lex->token != TOKEN_STRING) { + error_set(error, lex, json_error_invalid_syntax, "string or '}' expected"); + goto error; + } + + key = lex_steal_string(lex, &len); + if (!key) + return NULL; + if (memchr(key, '\0', len)) { + jsonp_free(key); + error_set(error, lex, json_error_null_byte_in_key, + "NUL byte in object key not supported"); + goto error; + } + + if (flags & JSON_REJECT_DUPLICATES) { + if (json_object_getn(object, key, len)) { + jsonp_free(key); + error_set(error, lex, json_error_duplicate_key, "duplicate object key"); + goto error; + } + } + + lex_scan(lex, error); + if (lex->token != ':') { + jsonp_free(key); + error_set(error, lex, json_error_invalid_syntax, "':' expected"); + goto error; + } + + lex_scan(lex, error); + value = parse_value(lex, flags, error); + if (!value) { + jsonp_free(key); + goto error; + } + + if (json_object_setn_new_nocheck(object, key, len, value)) { + jsonp_free(key); + goto error; + } + + jsonp_free(key); + + lex_scan(lex, error); + if (lex->token != ',') + break; + + lex_scan(lex, error); + } + + if (lex->token != '}') { + error_set(error, lex, json_error_invalid_syntax, "'}' expected"); + goto error; + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) { + json_t *array = json_array(); + if (!array) + return NULL; + + lex_scan(lex, error); + if (lex->token == ']') + return array; + + while (lex->token) { + json_t *elem = parse_value(lex, flags, error); + if (!elem) + goto error; + + if (json_array_append_new(array, elem)) { + goto error; + } + + lex_scan(lex, error); + if (lex->token != ',') + break; + + lex_scan(lex, error); + } + + if (lex->token != ']') { + error_set(error, lex, json_error_invalid_syntax, "']' expected"); + goto error; + } + + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) { + json_t *json; + + lex->depth++; + if (lex->depth > JSON_PARSER_MAX_DEPTH) { + error_set(error, lex, json_error_stack_overflow, "maximum parsing depth reached"); + return NULL; + } + + switch (lex->token) { + case TOKEN_STRING: { + const char *value = lex->value.string.val; + size_t len = lex->value.string.len; + + if (!(flags & JSON_ALLOW_NUL)) { + if (memchr(value, '\0', len)) { + error_set(error, lex, json_error_null_character, + "\\u0000 is not allowed without JSON_ALLOW_NUL"); + return NULL; + } + } + + json = jsonp_stringn_nocheck_own(value, len); + lex->value.string.val = NULL; + lex->value.string.len = 0; + break; + } + + case TOKEN_INTEGER: { + json = json_integer(lex->value.integer); + break; + } + + case TOKEN_REAL: { + json = json_real(lex->value.real); + break; + } + + case TOKEN_TRUE: + json = json_true(); + break; + + case TOKEN_FALSE: + json = json_false(); + break; + + case TOKEN_NULL: + json = json_null(); + break; + + case '{': + json = parse_object(lex, flags, error); + break; + + case '[': + json = parse_array(lex, flags, error); + break; + + case TOKEN_INVALID: + error_set(error, lex, json_error_invalid_syntax, "invalid token"); + return NULL; + + default: + error_set(error, lex, json_error_invalid_syntax, "unexpected token"); + return NULL; + } + + if (!json) + return NULL; + + lex->depth--; + return json; +} + +static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) { + json_t *result; + + lex->depth = 0; + + lex_scan(lex, error); + if (!(flags & JSON_DECODE_ANY)) { + if (lex->token != '[' && lex->token != '{') { + error_set(error, lex, json_error_invalid_syntax, "'[' or '{' expected"); + return NULL; + } + } + + result = parse_value(lex, flags, error); + if (!result) + return NULL; + + if (!(flags & JSON_DISABLE_EOF_CHECK)) { + lex_scan(lex, error); + if (lex->token != TOKEN_EOF) { + error_set(error, lex, json_error_end_of_input_expected, + "end of file expected"); + json_decref(result); + return NULL; + } + } + + if (error) { + /* Save the position even though there was no error */ + error->position = (int)lex->stream.position; + } + + return result; +} + +typedef struct { + const char *data; + size_t pos; +} string_data_t; + +static int string_get(void *data) { + char c; + string_data_t *stream = (string_data_t *)data; + c = stream->data[stream->pos]; + if (c == '\0') + return EOF; + else { + stream->pos++; + return (unsigned char)c; + } +} + +json_t *json_loads(const char *string, size_t flags, json_error_t *error) { + lex_t lex; + json_t *result; + string_data_t stream_data; + + jsonp_error_init(error, ""); + + if (string == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + stream_data.data = string; + stream_data.pos = 0; + + if (lex_init(&lex, string_get, flags, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +typedef struct { + const char *data; + size_t len; + size_t pos; +} buffer_data_t; + +static int buffer_get(void *data) { + char c; + buffer_data_t *stream = data; + if (stream->pos >= stream->len) + return EOF; + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) { + lex_t lex; + json_t *result; + buffer_data_t stream_data; + + jsonp_error_init(error, ""); + + if (buffer == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + stream_data.data = buffer; + stream_data.pos = 0; + stream_data.len = buflen; + + if (lex_init(&lex, buffer_get, flags, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) { + lex_t lex; + const char *source; + json_t *result; + + if (input == stdin) + source = ""; + else + source = ""; + + jsonp_error_init(error, source); + + if (input == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if (lex_init(&lex, (get_func)fgetc, flags, input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +static int fd_get_func(int *fd) { +#ifdef HAVE_UNISTD_H + uint8_t c; + if (read(*fd, &c, 1) == 1) + return c; +#endif + return EOF; +} + +json_t *json_loadfd(int input, size_t flags, json_error_t *error) { + lex_t lex; + const char *source; + json_t *result; + +#ifdef HAVE_UNISTD_H + if (input == STDIN_FILENO) + source = ""; + else +#endif + source = ""; + + jsonp_error_init(error, source); + + if (input < 0) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if (lex_init(&lex, (get_func)fd_get_func, flags, &input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) { + json_t *result; + FILE *fp; + + jsonp_error_init(error, path); + + if (path == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + fp = fopen(path, "rb"); + if (!fp) { + error_set(error, NULL, json_error_cannot_open_file, "unable to open %s: %s", path, + strerror(errno)); + return NULL; + } + + result = json_loadf(fp, flags, error); + + fclose(fp); + return result; +} + +#define MAX_BUF_LEN 1024 + +typedef struct { + char data[MAX_BUF_LEN]; + size_t len; + size_t pos; + json_load_callback_t callback; + void *arg; +} callback_data_t; + +static int callback_get(void *data) { + char c; + callback_data_t *stream = data; + + if (stream->pos >= stream->len) { + stream->pos = 0; + stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); + if (stream->len == 0 || stream->len == (size_t)-1) + return EOF; + } + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, + json_error_t *error) { + lex_t lex; + json_t *result; + + callback_data_t stream_data; + + memset(&stream_data, 0, sizeof(stream_data)); + stream_data.callback = callback; + stream_data.arg = arg; + + jsonp_error_init(error, ""); + + if (callback == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if (lex_init(&lex, (get_func)callback_get, flags, &stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} diff --git a/lib/jansson/src/lookup3.h b/lib/jansson/src/lookup3.h new file mode 100644 index 0000000..9b39aa1 --- /dev/null +++ b/lib/jansson/src/lookup3.h @@ -0,0 +1,382 @@ +// clang-format off +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include /* defines uint32_t etc */ +#endif + +#ifdef HAVE_SYS_PARAM_H +#include /* attempt to define endianness */ +#endif + +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((size_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 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). + */ +#ifndef NO_MASKING_TRICK + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; /* fall through */ + case 11: c+=((uint32_t)k[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k[9])<<8; /* fall through */ + case 9 : c+=k[8]; /* fall through */ + case 8 : b+=((uint32_t)k[7])<<24; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<8; /* fall through */ + case 5 : b+=k[4]; /* fall through */ + case 4 : a+=((uint32_t)k[3])<<24; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<8; /* fall through */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} diff --git a/lib/jansson/src/memory.c b/lib/jansson/src/memory.c new file mode 100644 index 0000000..2733035 --- /dev/null +++ b/lib/jansson/src/memory.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2011-2012 Basile Starynkevitch + * + * Jansson is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include + +#include "jansson.h" +#include "jansson_private.h" + +/* C89 allows these to be macros */ +#undef malloc +#undef free + +/* memory function pointers */ +static json_malloc_t do_malloc = malloc; +static json_free_t do_free = free; + +void *jsonp_malloc(size_t size) { + if (!size) + return NULL; + + return (*do_malloc)(size); +} + +void jsonp_free(void *ptr) { + if (!ptr) + return; + + (*do_free)(ptr); +} + +char *jsonp_strdup(const char *str) { return jsonp_strndup(str, strlen(str)); } + +char *jsonp_strndup(const char *str, size_t len) { + char *new_str; + + new_str = jsonp_malloc(len + 1); + if (!new_str) + return NULL; + + memcpy(new_str, str, len); + new_str[len] = '\0'; + return new_str; +} + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) { + do_malloc = malloc_fn; + do_free = free_fn; +} + +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn) { + if (malloc_fn) + *malloc_fn = do_malloc; + if (free_fn) + *free_fn = do_free; +} diff --git a/lib/jansson/src/pack_unpack.c b/lib/jansson/src/pack_unpack.c new file mode 100644 index 0000000..a796ed4 --- /dev/null +++ b/lib/jansson/src/pack_unpack.c @@ -0,0 +1,936 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2011-2012 Graeme Smecher + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" +#include + +typedef struct { + int line; + int column; + size_t pos; + char token; +} token_t; + +typedef struct { + const char *start; + const char *fmt; + token_t prev_token; + token_t token; + token_t next_token; + json_error_t *error; + size_t flags; + int line; + int column; + size_t pos; + int has_error; +} scanner_t; + +#define token(scanner) ((scanner)->token.token) + +static const char *const type_names[] = {"object", "array", "string", "integer", + "real", "true", "false", "null"}; + +#define type_name(x) type_names[json_typeof(x)] + +static const char unpack_value_starters[] = "{[siIbfFOon"; + +static void scanner_init(scanner_t *s, json_error_t *error, size_t flags, + const char *fmt) { + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + memset(&s->prev_token, 0, sizeof(token_t)); + memset(&s->token, 0, sizeof(token_t)); + memset(&s->next_token, 0, sizeof(token_t)); + s->line = 1; + s->column = 0; + s->pos = 0; + s->has_error = 0; +} + +static void next_token(scanner_t *s) { + const char *t; + s->prev_token = s->token; + + if (s->next_token.line) { + s->token = s->next_token; + s->next_token.line = 0; + return; + } + + if (!token(s) && !*s->fmt) + return; + + t = s->fmt; + s->column++; + s->pos++; + + /* skip space and ignored chars */ + while (*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { + if (*t == '\n') { + s->line++; + s->column = 1; + } else + s->column++; + + s->pos++; + t++; + } + + s->token.token = *t; + s->token.line = s->line; + s->token.column = s->column; + s->token.pos = s->pos; + + if (*t) + t++; + s->fmt = t; +} + +static void prev_token(scanner_t *s) { + s->next_token = s->token; + s->token = s->prev_token; +} + +static void set_error(scanner_t *s, const char *source, enum json_error_code code, + const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + + jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, code, fmt, + ap); + + jsonp_error_set_source(s->error, source); + + va_end(ap); +} + +static json_t *pack(scanner_t *s, va_list *ap); + +/* ours will be set to 1 if jsonp_free() must be called for the result + afterwards */ +static char *read_string(scanner_t *s, va_list *ap, const char *purpose, size_t *out_len, + int *ours, int optional) { + char t; + strbuffer_t strbuff; + const char *str; + size_t length; + + next_token(s); + t = token(s); + prev_token(s); + + *ours = 0; + if (t != '#' && t != '%' && t != '+') { + /* Optimize the simple case */ + str = va_arg(*ap, const char *); + + if (!str) { + if (!optional) { + set_error(s, "", json_error_null_value, "NULL %s", purpose); + s->has_error = 1; + } + return NULL; + } + + length = strlen(str); + + if (!utf8_check_string(str, length)) { + set_error(s, "", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose); + s->has_error = 1; + return NULL; + } + + *out_len = length; + return (char *)str; + } else if (optional) { + set_error(s, "", json_error_invalid_format, + "Cannot use '%c' on optional strings", t); + s->has_error = 1; + + return NULL; + } + + if (strbuffer_init(&strbuff)) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + s->has_error = 1; + } + + while (1) { + str = va_arg(*ap, const char *); + if (!str) { + set_error(s, "", json_error_null_value, "NULL %s", purpose); + s->has_error = 1; + } + + next_token(s); + + if (token(s) == '#') { + length = va_arg(*ap, int); + } else if (token(s) == '%') { + length = va_arg(*ap, size_t); + } else { + prev_token(s); + length = s->has_error ? 0 : strlen(str); + } + + if (!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + s->has_error = 1; + } + + next_token(s); + if (token(s) != '+') { + prev_token(s); + break; + } + } + + if (s->has_error) { + strbuffer_close(&strbuff); + return NULL; + } + + if (!utf8_check_string(strbuff.value, strbuff.length)) { + set_error(s, "", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose); + strbuffer_close(&strbuff); + s->has_error = 1; + return NULL; + } + + *out_len = strbuff.length; + *ours = 1; + return strbuffer_steal_value(&strbuff); +} + +static json_t *pack_object(scanner_t *s, va_list *ap) { + json_t *object = json_object(); + next_token(s); + + while (token(s) != '}') { + char *key; + size_t len; + int ours; + json_t *value; + char valueOptional; + + if (!token(s)) { + set_error(s, "", json_error_invalid_format, + "Unexpected end of format string"); + goto error; + } + + if (token(s) != 's') { + set_error(s, "", json_error_invalid_format, + "Expected format 's', got '%c'", token(s)); + goto error; + } + + key = read_string(s, ap, "object key", &len, &ours, 0); + + next_token(s); + + next_token(s); + valueOptional = token(s); + prev_token(s); + + value = pack(s, ap); + if (!value) { + if (ours) + jsonp_free(key); + + if (valueOptional != '*') { + set_error(s, "", json_error_null_value, "NULL object value"); + s->has_error = 1; + } + + next_token(s); + continue; + } + + if (s->has_error) + json_decref(value); + + if (!s->has_error && json_object_set_new_nocheck(object, key, value)) { + set_error(s, "", json_error_out_of_memory, + "Unable to add key \"%s\"", key); + s->has_error = 1; + } + + if (ours) + jsonp_free(key); + + next_token(s); + } + + if (!s->has_error) + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *pack_array(scanner_t *s, va_list *ap) { + json_t *array = json_array(); + next_token(s); + + while (token(s) != ']') { + json_t *value; + char valueOptional; + + if (!token(s)) { + set_error(s, "", json_error_invalid_format, + "Unexpected end of format string"); + /* Format string errors are unrecoverable. */ + goto error; + } + + next_token(s); + valueOptional = token(s); + prev_token(s); + + value = pack(s, ap); + if (!value) { + if (valueOptional != '*') { + s->has_error = 1; + } + + next_token(s); + continue; + } + + if (s->has_error) + json_decref(value); + + if (!s->has_error && json_array_append_new(array, value)) { + set_error(s, "", json_error_out_of_memory, + "Unable to append to array"); + s->has_error = 1; + } + + next_token(s); + } + + if (!s->has_error) + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *pack_string(scanner_t *s, va_list *ap) { + char *str; + char t; + size_t len; + int ours; + int optional; + + next_token(s); + t = token(s); + optional = t == '?' || t == '*'; + if (!optional) + prev_token(s); + + str = read_string(s, ap, "string", &len, &ours, optional); + + if (!str) + return t == '?' && !s->has_error ? json_null() : NULL; + + if (s->has_error) { + /* It's impossible to reach this point if ours != 0, do not free str. */ + return NULL; + } + + if (ours) + return jsonp_stringn_nocheck_own(str, len); + + return json_stringn_nocheck(str, len); +} + +static json_t *pack_object_inter(scanner_t *s, va_list *ap, int need_incref) { + json_t *json; + char ntoken; + + next_token(s); + ntoken = token(s); + + if (ntoken != '?' && ntoken != '*') + prev_token(s); + + json = va_arg(*ap, json_t *); + + if (json) + return need_incref ? json_incref(json) : json; + + switch (ntoken) { + case '?': + return json_null(); + case '*': + return NULL; + default: + break; + } + + set_error(s, "", json_error_null_value, "NULL object"); + s->has_error = 1; + return NULL; +} + +static json_t *pack_integer(scanner_t *s, json_int_t value) { + json_t *json = json_integer(value); + + if (!json) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + s->has_error = 1; + } + + return json; +} + +static json_t *pack_real(scanner_t *s, double value) { + /* Allocate without setting value so we can identify OOM error. */ + json_t *json = json_real(0.0); + + if (!json) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + s->has_error = 1; + + return NULL; + } + + if (json_real_set(json, value)) { + json_decref(json); + + set_error(s, "", json_error_numeric_overflow, + "Invalid floating point value"); + s->has_error = 1; + + return NULL; + } + + return json; +} + +static json_t *pack(scanner_t *s, va_list *ap) { + switch (token(s)) { + case '{': + return pack_object(s, ap); + + case '[': + return pack_array(s, ap); + + case 's': /* string */ + return pack_string(s, ap); + + case 'n': /* null */ + return json_null(); + + case 'b': /* boolean */ + return va_arg(*ap, int) ? json_true() : json_false(); + + case 'i': /* integer from int */ + return pack_integer(s, va_arg(*ap, int)); + + case 'I': /* integer from json_int_t */ + return pack_integer(s, va_arg(*ap, json_int_t)); + + case 'f': /* real */ + return pack_real(s, va_arg(*ap, double)); + + case 'O': /* a json_t object; increments refcount */ + return pack_object_inter(s, ap, 1); + + case 'o': /* a json_t object; doesn't increment refcount */ + return pack_object_inter(s, ap, 0); + + default: + set_error(s, "", json_error_invalid_format, + "Unexpected format character '%c'", token(s)); + s->has_error = 1; + return NULL; + } +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap); + +static int unpack_object(scanner_t *s, json_t *root, va_list *ap) { + int ret = -1; + int strict = 0; + int gotopt = 0; + + /* Use a set (emulated by a hashtable) to check that all object + keys are accessed. Checking that the correct number of keys + were accessed is not enough, as the same key can be unpacked + multiple times. + */ + hashtable_t key_set; + + if (hashtable_init(&key_set)) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + return -1; + } + + if (root && !json_is_object(root)) { + set_error(s, "", json_error_wrong_type, "Expected object, got %s", + type_name(root)); + goto out; + } + next_token(s); + + while (token(s) != '}') { + const char *key; + json_t *value; + int opt = 0; + + if (strict != 0) { + set_error(s, "", json_error_invalid_format, + "Expected '}' after '%c', got '%c'", (strict == 1 ? '!' : '*'), + token(s)); + goto out; + } + + if (!token(s)) { + set_error(s, "", json_error_invalid_format, + "Unexpected end of format string"); + goto out; + } + + if (token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if (token(s) != 's') { + set_error(s, "", json_error_invalid_format, + "Expected format 's', got '%c'", token(s)); + goto out; + } + + key = va_arg(*ap, const char *); + if (!key) { + set_error(s, "", json_error_null_value, "NULL object key"); + goto out; + } + + next_token(s); + + if (token(s) == '?') { + opt = gotopt = 1; + next_token(s); + } + + if (!root) { + /* skipping */ + value = NULL; + } else { + value = json_object_get(root, key); + if (!value && !opt) { + set_error(s, "", json_error_item_not_found, + "Object item not found: %s", key); + goto out; + } + } + + if (unpack(s, value, ap)) + goto out; + + hashtable_set(&key_set, key, strlen(key), json_null()); + next_token(s); + } + + if (strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if (root && strict == 1) { + /* We need to check that all non optional items have been parsed */ + const char *key; + size_t key_len; + /* keys_res is 1 for uninitialized, 0 for success, -1 for error. */ + int keys_res = 1; + strbuffer_t unrecognized_keys; + json_t *value; + long unpacked = 0; + + if (gotopt || json_object_size(root) != key_set.size) { + json_object_keylen_foreach(root, key, key_len, value) { + if (!hashtable_get(&key_set, key, key_len)) { + unpacked++; + + /* Save unrecognized keys for the error message */ + if (keys_res == 1) { + keys_res = strbuffer_init(&unrecognized_keys); + } else if (!keys_res) { + keys_res = strbuffer_append_bytes(&unrecognized_keys, ", ", 2); + } + + if (!keys_res) + keys_res = + strbuffer_append_bytes(&unrecognized_keys, key, key_len); + } + } + } + if (unpacked) { + set_error(s, "", json_error_end_of_input_expected, + "%li object item(s) left unpacked: %s", unpacked, + keys_res ? "" : strbuffer_value(&unrecognized_keys)); + strbuffer_close(&unrecognized_keys); + goto out; + } + } + + ret = 0; + +out: + hashtable_close(&key_set); + return ret; +} + +static int unpack_array(scanner_t *s, json_t *root, va_list *ap) { + size_t i = 0; + int strict = 0; + + if (root && !json_is_array(root)) { + set_error(s, "", json_error_wrong_type, "Expected array, got %s", + type_name(root)); + return -1; + } + next_token(s); + + while (token(s) != ']') { + json_t *value; + + if (strict != 0) { + set_error(s, "", json_error_invalid_format, + "Expected ']' after '%c', got '%c'", (strict == 1 ? '!' : '*'), + token(s)); + return -1; + } + + if (!token(s)) { + set_error(s, "", json_error_invalid_format, + "Unexpected end of format string"); + return -1; + } + + if (token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if (!strchr(unpack_value_starters, token(s))) { + set_error(s, "", json_error_invalid_format, + "Unexpected format character '%c'", token(s)); + return -1; + } + + if (!root) { + /* skipping */ + value = NULL; + } else { + value = json_array_get(root, i); + if (!value) { + set_error(s, "", json_error_index_out_of_range, + "Array index %lu out of range", (unsigned long)i); + return -1; + } + } + + if (unpack(s, value, ap)) + return -1; + + next_token(s); + i++; + } + + if (strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if (root && strict == 1 && i != json_array_size(root)) { + long diff = (long)json_array_size(root) - (long)i; + set_error(s, "", json_error_end_of_input_expected, + "%li array item(s) left unpacked", diff); + return -1; + } + + return 0; +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap) { + switch (token(s)) { + case '{': + return unpack_object(s, root, ap); + + case '[': + return unpack_array(s, root, ap); + + case 's': + if (root && !json_is_string(root)) { + set_error(s, "", json_error_wrong_type, + "Expected string, got %s", type_name(root)); + return -1; + } + + if (!(s->flags & JSON_VALIDATE_ONLY)) { + const char **str_target; + size_t *len_target = NULL; + + str_target = va_arg(*ap, const char **); + if (!str_target) { + set_error(s, "", json_error_null_value, "NULL string argument"); + return -1; + } + + next_token(s); + + if (token(s) == '%') { + len_target = va_arg(*ap, size_t *); + if (!len_target) { + set_error(s, "", json_error_null_value, + "NULL string length argument"); + return -1; + } + } else + prev_token(s); + + if (root) { + *str_target = json_string_value(root); + if (len_target) + *len_target = json_string_length(root); + } + } + return 0; + + case 'i': + if (root && !json_is_integer(root)) { + set_error(s, "", json_error_wrong_type, + "Expected integer, got %s", type_name(root)); + return -1; + } + + if (!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int *); + if (root) + *target = (int)json_integer_value(root); + } + + return 0; + + case 'I': + if (root && !json_is_integer(root)) { + set_error(s, "", json_error_wrong_type, + "Expected integer, got %s", type_name(root)); + return -1; + } + + if (!(s->flags & JSON_VALIDATE_ONLY)) { + json_int_t *target = va_arg(*ap, json_int_t *); + if (root) + *target = json_integer_value(root); + } + + return 0; + + case 'b': + if (root && !json_is_boolean(root)) { + set_error(s, "", json_error_wrong_type, + "Expected true or false, got %s", type_name(root)); + return -1; + } + + if (!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int *); + if (root) + *target = json_is_true(root); + } + + return 0; + + case 'f': + if (root && !json_is_real(root)) { + set_error(s, "", json_error_wrong_type, + "Expected real, got %s", type_name(root)); + return -1; + } + + if (!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double *); + if (root) + *target = json_real_value(root); + } + + return 0; + + case 'F': + if (root && !json_is_number(root)) { + set_error(s, "", json_error_wrong_type, + "Expected real or integer, got %s", type_name(root)); + return -1; + } + + if (!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double *); + if (root) + *target = json_number_value(root); + } + + return 0; + + case 'O': + if (root && !(s->flags & JSON_VALIDATE_ONLY)) + json_incref(root); + /* Fall through */ + + case 'o': + if (!(s->flags & JSON_VALIDATE_ONLY)) { + json_t **target = va_arg(*ap, json_t **); + if (root) + *target = root; + } + + return 0; + + case 'n': + /* Never assign, just validate */ + if (root && !json_is_null(root)) { + set_error(s, "", json_error_wrong_type, + "Expected null, got %s", type_name(root)); + return -1; + } + return 0; + + default: + set_error(s, "", json_error_invalid_format, + "Unexpected format character '%c'", token(s)); + return -1; + } +} + +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) { + scanner_t s; + va_list ap_copy; + json_t *value; + + if (!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, + "NULL or empty format string"); + return NULL; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + value = pack(&s, &ap_copy); + va_end(ap_copy); + + /* This will cover all situations where s.has_error is true */ + if (!value) + return NULL; + + next_token(&s); + if (token(&s)) { + json_decref(value); + set_error(&s, "", json_error_invalid_format, + "Garbage after format string"); + return NULL; + } + + return value; +} + +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) { + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(error, flags, fmt, ap); + va_end(ap); + + return value; +} + +json_t *json_pack(const char *fmt, ...) { + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(NULL, 0, fmt, ap); + va_end(ap); + + return value; +} + +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, + va_list ap) { + scanner_t s; + va_list ap_copy; + + if (!root) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value"); + return -1; + } + + if (!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, + "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + if (unpack(&s, root, &ap_copy)) { + va_end(ap_copy); + return -1; + } + va_end(ap_copy); + + next_token(&s); + if (token(&s)) { + set_error(&s, "", json_error_invalid_format, + "Garbage after format string"); + return -1; + } + + return 0; +} + +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, + ...) { + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, error, flags, fmt, ap); + va_end(ap); + + return ret; +} + +int json_unpack(json_t *root, const char *fmt, ...) { + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, NULL, 0, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/lib/jansson/src/strbuffer.c b/lib/jansson/src/strbuffer.c new file mode 100644 index 0000000..d9bcd43 --- /dev/null +++ b/lib/jansson/src/strbuffer.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "strbuffer.h" +#include "jansson_private.h" +#include +#include + +#define STRBUFFER_MIN_SIZE 16 +#define STRBUFFER_FACTOR 2 +#define STRBUFFER_SIZE_MAX ((size_t)-1) + +int strbuffer_init(strbuffer_t *strbuff) { + strbuff->size = STRBUFFER_MIN_SIZE; + strbuff->length = 0; + + strbuff->value = jsonp_malloc(strbuff->size); + if (!strbuff->value) + return -1; + + /* initialize to empty */ + strbuff->value[0] = '\0'; + return 0; +} + +void strbuffer_close(strbuffer_t *strbuff) { + if (strbuff->value) + jsonp_free(strbuff->value); + + strbuff->size = 0; + strbuff->length = 0; + strbuff->value = NULL; +} + +void strbuffer_clear(strbuffer_t *strbuff) { + strbuff->length = 0; + strbuff->value[0] = '\0'; +} + +const char *strbuffer_value(const strbuffer_t *strbuff) { return strbuff->value; } + +char *strbuffer_steal_value(strbuffer_t *strbuff) { + char *result = strbuff->value; + strbuff->value = NULL; + return result; +} + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte) { + return strbuffer_append_bytes(strbuff, &byte, 1); +} + +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) { + if (size >= strbuff->size - strbuff->length) { + size_t new_size; + char *new_value; + + /* avoid integer overflow */ + if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR || + size > STRBUFFER_SIZE_MAX - 1 || + strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) + return -1; + + new_size = max(strbuff->size * STRBUFFER_FACTOR, strbuff->length + size + 1); + + new_value = jsonp_malloc(new_size); + if (!new_value) + return -1; + + memcpy(new_value, strbuff->value, strbuff->length); + + jsonp_free(strbuff->value); + strbuff->value = new_value; + strbuff->size = new_size; + } + + memcpy(strbuff->value + strbuff->length, data, size); + strbuff->length += size; + strbuff->value[strbuff->length] = '\0'; + + return 0; +} + +char strbuffer_pop(strbuffer_t *strbuff) { + if (strbuff->length > 0) { + char c = strbuff->value[--strbuff->length]; + strbuff->value[strbuff->length] = '\0'; + return c; + } else + return '\0'; +} diff --git a/lib/jansson/src/strbuffer.h b/lib/jansson/src/strbuffer.h new file mode 100644 index 0000000..70f2646 --- /dev/null +++ b/lib/jansson/src/strbuffer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef STRBUFFER_H +#define STRBUFFER_H + +#include "jansson.h" +#include + +typedef struct { + char *value; + size_t length; /* bytes used */ + size_t size; /* bytes allocated */ +} strbuffer_t; + +int strbuffer_init(strbuffer_t *strbuff) JANSSON_ATTRS((warn_unused_result)); +void strbuffer_close(strbuffer_t *strbuff); + +void strbuffer_clear(strbuffer_t *strbuff); + +const char *strbuffer_value(const strbuffer_t *strbuff); + +/* Steal the value and close the strbuffer */ +char *strbuffer_steal_value(strbuffer_t *strbuff); + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte); +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); + +char strbuffer_pop(strbuffer_t *strbuff); + +#endif diff --git a/lib/jansson/src/strconv.c b/lib/jansson/src/strconv.c new file mode 100644 index 0000000..9e202c5 --- /dev/null +++ b/lib/jansson/src/strconv.c @@ -0,0 +1,237 @@ +#include "jansson_private.h" +#include "strbuffer.h" +#include +#include +#include +#include +#include + +/* need jansson_private_config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include +#endif + +/* + - This code assumes that the decimal separator is exactly one + character. + + - If setlocale() is called by another thread between the call to + get_decimal_point() and the call to sprintf() or strtod(), the + result may be wrong. setlocale() is not thread-safe and should + not be used this way. Multi-threaded programs should use + uselocale() instead. +*/ +static char get_decimal_point() { + char buf[3]; + sprintf(buf, "%#.0f", 1.0); // "1." in the current locale + return buf[1]; +} + +static void to_locale(strbuffer_t *strbuffer) { + char point; + char *pos; + + point = get_decimal_point(); + if (point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(strbuffer->value, '.'); + if (pos) + *pos = point; +} + +int jsonp_strtod(strbuffer_t *strbuffer, double *out) { + double value; + char *end; + + to_locale(strbuffer); + + errno = 0; + value = strtod(strbuffer->value, &end); + assert(end == strbuffer->value + strbuffer->length); + + if ((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) { + /* Overflow */ + return -1; + } + + *out = value; + return 0; +} + +#if DTOA_ENABLED +/* see dtoa.c */ +char *dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve, + char *buf, size_t blen); + +int jsonp_dtostr(char *buffer, size_t size, double value, int precision) { + /* adapted from `format_float_short()` in + * https://github.com/python/cpython/blob/2cf18a44303b6d84faa8ecffaecc427b53ae121e/Python/pystrtod.c#L969 + */ + char digits[25]; + char *digits_end; + int mode = precision == 0 ? 0 : 2; + int decpt, sign, exp_len, exp = 0, use_exp = 0; + int digits_len, vdigits_start, vdigits_end; + char *p; + + if (dtoa_r(value, mode, precision, &decpt, &sign, &digits_end, digits, 25) == NULL) { + // digits is too short => should not happen + return -1; + } + + digits_len = digits_end - digits; + if (decpt <= -4 || decpt > 16) { + use_exp = 1; + exp = decpt - 1; + decpt = 1; + } + + vdigits_start = decpt <= 0 ? decpt - 1 : 0; + vdigits_end = digits_len; + if (!use_exp) { + /* decpt + 1 to add ".0" if value is an integer */ + vdigits_end = vdigits_end > decpt ? vdigits_end : decpt + 1; + } else { + vdigits_end = vdigits_end > decpt ? vdigits_end : decpt; + } + + if ( + /* sign, decimal point and trailing 0 byte */ + (size_t)(3 + + + /* total digit count (including zero padding on both sides) */ + (vdigits_end - vdigits_start) + + + /* exponent "e+100", max 3 numerical digits */ + (use_exp ? 5 : 0)) > size) { + /* buffer is too short */ + return -1; + } + + p = buffer; + if (sign == 1) { + *p++ = '-'; + } + + /* note that exactly one of the three 'if' conditions is true, + so we include exactly one decimal point */ + /* Zero padding on left of digit string */ + if (decpt <= 0) { + memset(p, '0', decpt - vdigits_start); + p += decpt - vdigits_start; + *p++ = '.'; + memset(p, '0', 0 - decpt); + p += 0 - decpt; + } else { + memset(p, '0', 0 - vdigits_start); + p += 0 - vdigits_start; + } + + /* Digits, with included decimal point */ + if (0 < decpt && decpt <= digits_len) { + strncpy(p, digits, decpt - 0); + p += decpt - 0; + *p++ = '.'; + strncpy(p, digits + decpt, digits_len - decpt); + p += digits_len - decpt; + } else { + strncpy(p, digits, digits_len); + p += digits_len; + } + + /* And zeros on the right */ + if (digits_len < decpt) { + memset(p, '0', decpt - digits_len); + p += decpt - digits_len; + *p++ = '.'; + memset(p, '0', vdigits_end - decpt); + p += vdigits_end - decpt; + } else { + memset(p, '0', vdigits_end - digits_len); + p += vdigits_end - digits_len; + } + + if (p[-1] == '.') + p--; + + if (use_exp) { + *p++ = 'e'; + exp_len = sprintf(p, "%d", exp); + p += exp_len; + } + *p = '\0'; + + return (int)(p - buffer); +} +#else /* DTOA_ENABLED == 0 */ +static void from_locale(char *buffer) { + char point; + char *pos; + + point = get_decimal_point(); + if (point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(buffer, point); + if (pos) + *pos = '.'; +} + +int jsonp_dtostr(char *buffer, size_t size, double value, int precision) { + int ret; + char *start, *end; + size_t length; + + if (precision == 0) + precision = 17; + + ret = snprintf(buffer, size, "%.*g", precision, value); + if (ret < 0) + return -1; + + length = (size_t)ret; + if (length >= size) + return -1; + + from_locale(buffer); + + /* Make sure there's a dot or 'e' in the output. Otherwise + a real is converted to an integer when decoding */ + if (strchr(buffer, '.') == NULL && strchr(buffer, 'e') == NULL) { + if (length + 3 >= size) { + /* No space to append ".0" */ + return -1; + } + buffer[length] = '.'; + buffer[length + 1] = '0'; + buffer[length + 2] = '\0'; + length += 2; + } + + /* Remove leading '+' from positive exponent. Also remove leading + zeros from exponents (added by some printf() implementations) */ + start = strchr(buffer, 'e'); + if (start) { + start++; + end = start + 1; + + if (*start == '-') + start++; + + while (*end == '0') + end++; + + if (end != start) { + memmove(start, end, length - (size_t)(end - buffer)); + length -= (size_t)(end - start); + } + } + + return (int)length; +} +#endif diff --git a/lib/jansson/src/utf.c b/lib/jansson/src/utf.c new file mode 100644 index 0000000..28b2f7d --- /dev/null +++ b/lib/jansson/src/utf.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "utf.h" +#include + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size) { + if (codepoint < 0) + return -1; + else if (codepoint < 0x80) { + buffer[0] = (char)codepoint; + *size = 1; + } else if (codepoint < 0x800) { + buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); + buffer[1] = 0x80 + ((codepoint & 0x03F)); + *size = 2; + } else if (codepoint < 0x10000) { + buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); + buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); + buffer[2] = 0x80 + ((codepoint & 0x003F)); + *size = 3; + } else if (codepoint <= 0x10FFFF) { + buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); + buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); + buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); + buffer[3] = 0x80 + ((codepoint & 0x00003F)); + *size = 4; + } else + return -1; + + return 0; +} + +size_t utf8_check_first(char byte) { + unsigned char u = (unsigned char)byte; + + if (u < 0x80) + return 1; + + if (0x80 <= u && u <= 0xBF) { + /* second, third or fourth byte of a multi-byte + sequence, i.e. a "continuation byte" */ + return 0; + } else if (u == 0xC0 || u == 0xC1) { + /* overlong encoding of an ASCII byte */ + return 0; + } else if (0xC2 <= u && u <= 0xDF) { + /* 2-byte sequence */ + return 2; + } + + else if (0xE0 <= u && u <= 0xEF) { + /* 3-byte sequence */ + return 3; + } else if (0xF0 <= u && u <= 0xF4) { + /* 4-byte sequence */ + return 4; + } else { /* u >= 0xF5 */ + /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid + UTF-8 */ + return 0; + } +} + +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint) { + size_t i; + int32_t value = 0; + unsigned char u = (unsigned char)buffer[0]; + + if (size == 2) { + value = u & 0x1F; + } else if (size == 3) { + value = u & 0xF; + } else if (size == 4) { + value = u & 0x7; + } else + return 0; + + for (i = 1; i < size; i++) { + u = (unsigned char)buffer[i]; + + if (u < 0x80 || u > 0xBF) { + /* not a continuation byte */ + return 0; + } + + value = (value << 6) + (u & 0x3F); + } + + if (value > 0x10FFFF) { + /* not in Unicode range */ + return 0; + } + + else if (0xD800 <= value && value <= 0xDFFF) { + /* invalid code point (UTF-16 surrogate halves) */ + return 0; + } + + else if ((size == 2 && value < 0x80) || (size == 3 && value < 0x800) || + (size == 4 && value < 0x10000)) { + /* overlong encoding */ + return 0; + } + + if (codepoint) + *codepoint = value; + + return 1; +} + +const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint) { + size_t count; + int32_t value; + + if (!bufsize) + return buffer; + + count = utf8_check_first(buffer[0]); + if (count <= 0) + return NULL; + + if (count == 1) + value = (unsigned char)buffer[0]; + else { + if (count > bufsize || !utf8_check_full(buffer, count, &value)) + return NULL; + } + + if (codepoint) + *codepoint = value; + + return buffer + count; +} + +int utf8_check_string(const char *string, size_t length) { + size_t i; + + for (i = 0; i < length; i++) { + size_t count = utf8_check_first(string[i]); + if (count == 0) + return 0; + else if (count > 1) { + if (count > length - i) + return 0; + + if (!utf8_check_full(&string[i], count, NULL)) + return 0; + + i += count - 1; + } + } + + return 1; +} diff --git a/lib/jansson/src/utf.h b/lib/jansson/src/utf.h new file mode 100644 index 0000000..5db99df --- /dev/null +++ b/lib/jansson/src/utf.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UTF_H +#define UTF_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#ifdef HAVE_STDINT_H +#include +#endif + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size); + +size_t utf8_check_first(char byte); +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint); +const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint); + +int utf8_check_string(const char *string, size_t length); + +#endif diff --git a/lib/jansson/src/value.c b/lib/jansson/src/value.c new file mode 100644 index 0000000..d5a11fe --- /dev/null +++ b/lib/jansson/src/value.c @@ -0,0 +1,1120 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#include "hashtable.h" +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" + +/* Work around nonstandard isnan() and isinf() implementations */ +#ifndef isnan +#ifndef __sun +static JSON_INLINE int isnan(double x) { return x != x; } +#endif +#endif +#ifndef isinf +static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif + +json_t *do_deep_copy(const json_t *json, hashtable_t *parents); + +static JSON_INLINE void json_init(json_t *json, json_type type) { + json->type = type; + json->refcount = 1; +} + +int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size, + size_t *key_len_out) { + size_t key_len = snprintf(key, key_size, "%p", json); + + if (key_len_out) + *key_len_out = key_len; + + if (hashtable_get(parents, key, key_len)) + return -1; + + return hashtable_set(parents, key, key_len, json_null()); +} + +/*** object ***/ + +extern volatile uint32_t hashtable_seed; + +json_t *json_object(void) { + json_object_t *object = jsonp_malloc(sizeof(json_object_t)); + if (!object) + return NULL; + + if (!hashtable_seed) { + /* Autoseed */ + json_object_seed(0); + } + + json_init(&object->json, JSON_OBJECT); + + if (hashtable_init(&object->hashtable)) { + jsonp_free(object); + return NULL; + } + + return &object->json; +} + +static void json_delete_object(json_object_t *object) { + hashtable_close(&object->hashtable); + jsonp_free(object); +} + +size_t json_object_size(const json_t *json) { + json_object_t *object; + + if (!json_is_object(json)) + return 0; + + object = json_to_object(json); + return object->hashtable.size; +} + +json_t *json_object_get(const json_t *json, const char *key) { + if (!key) + return NULL; + + return json_object_getn(json, key, strlen(key)); +} + +json_t *json_object_getn(const json_t *json, const char *key, size_t key_len) { + json_object_t *object; + + if (!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_get(&object->hashtable, key, key_len); +} + +int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) { + if (!key) { + json_decref(value); + return -1; + } + return json_object_setn_new_nocheck(json, key, strlen(key), value); +} + +int json_object_setn_new_nocheck(json_t *json, const char *key, size_t key_len, + json_t *value) { + json_object_t *object; + + if (!value) + return -1; + + if (!key || !json_is_object(json) || json == value) { + json_decref(value); + return -1; + } + object = json_to_object(json); + + if (hashtable_set(&object->hashtable, key, key_len, value)) { + json_decref(value); + return -1; + } + + return 0; +} + +int json_object_set_new(json_t *json, const char *key, json_t *value) { + if (!key) { + json_decref(value); + return -1; + } + + return json_object_setn_new(json, key, strlen(key), value); +} + +int json_object_setn_new(json_t *json, const char *key, size_t key_len, json_t *value) { + if (!key || !utf8_check_string(key, key_len)) { + json_decref(value); + return -1; + } + + return json_object_setn_new_nocheck(json, key, key_len, value); +} + +int json_object_del(json_t *json, const char *key) { + if (!key) + return -1; + + return json_object_deln(json, key, strlen(key)); +} + +int json_object_deln(json_t *json, const char *key, size_t key_len) { + json_object_t *object; + + if (!key || !json_is_object(json)) + return -1; + + object = json_to_object(json); + return hashtable_del(&object->hashtable, key, key_len); +} + +int json_object_clear(json_t *json) { + json_object_t *object; + + if (!json_is_object(json)) + return -1; + + object = json_to_object(json); + hashtable_clear(&object->hashtable); + + return 0; +} + +int json_object_update(json_t *object, json_t *other) { + const char *key; + size_t key_len; + json_t *value; + + if (!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_keylen_foreach(other, key, key_len, value) { + if (json_object_setn_nocheck(object, key, key_len, value)) + return -1; + } + + return 0; +} + +int json_object_update_existing(json_t *object, json_t *other) { + const char *key; + size_t key_len; + json_t *value; + + if (!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_keylen_foreach(other, key, key_len, value) { + if (json_object_getn(object, key, key_len)) + json_object_setn_nocheck(object, key, key_len, value); + } + + return 0; +} + +int json_object_update_missing(json_t *object, json_t *other) { + const char *key; + size_t key_len; + json_t *value; + + if (!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_keylen_foreach(other, key, key_len, value) { + if (!json_object_getn(object, key, key_len)) + json_object_setn_nocheck(object, key, key_len, value); + } + + return 0; +} + +int do_object_update_recursive(json_t *object, json_t *other, hashtable_t *parents) { + const char *key; + size_t key_len; + json_t *value; + char loop_key[LOOP_KEY_LEN]; + int res = 0; + size_t loop_key_len; + + if (!json_is_object(object) || !json_is_object(other)) + return -1; + + if (jsonp_loop_check(parents, other, loop_key, sizeof(loop_key), &loop_key_len)) + return -1; + + json_object_keylen_foreach(other, key, key_len, value) { + json_t *v = json_object_getn(object, key, key_len); + + if (json_is_object(v) && json_is_object(value)) { + if (do_object_update_recursive(v, value, parents)) { + res = -1; + break; + } + } else { + if (json_object_setn_nocheck(object, key, key_len, value)) { + res = -1; + break; + } + } + } + + hashtable_del(parents, loop_key, loop_key_len); + + return res; +} + +int json_object_update_recursive(json_t *object, json_t *other) { + int res; + hashtable_t parents_set; + + if (hashtable_init(&parents_set)) + return -1; + res = do_object_update_recursive(object, other, &parents_set); + hashtable_close(&parents_set); + + return res; +} + +void *json_object_iter(json_t *json) { + json_object_t *object; + + if (!json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter(&object->hashtable); +} + +void *json_object_iter_at(json_t *json, const char *key) { + json_object_t *object; + + if (!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter_at(&object->hashtable, key, strlen(key)); +} + +void *json_object_iter_next(json_t *json, void *iter) { + json_object_t *object; + + if (!json_is_object(json) || iter == NULL) + return NULL; + + object = json_to_object(json); + return hashtable_iter_next(&object->hashtable, iter); +} + +const char *json_object_iter_key(void *iter) { + if (!iter) + return NULL; + + return hashtable_iter_key(iter); +} + +size_t json_object_iter_key_len(void *iter) { + if (!iter) + return 0; + + return hashtable_iter_key_len(iter); +} + +json_t *json_object_iter_value(void *iter) { + if (!iter) + return NULL; + + return (json_t *)hashtable_iter_value(iter); +} + +int json_object_iter_set_new(json_t *json, void *iter, json_t *value) { + if (!json_is_object(json) || !iter || !value) { + json_decref(value); + return -1; + } + + hashtable_iter_set(iter, value); + return 0; +} + +void *json_object_key_to_iter(const char *key) { + if (!key) + return NULL; + + return hashtable_key_to_iter(key); +} + +static int json_object_equal(const json_t *object1, const json_t *object2) { + const char *key; + size_t key_len; + const json_t *value1, *value2; + + if (json_object_size(object1) != json_object_size(object2)) + return 0; + + json_object_keylen_foreach((json_t *)object1, key, key_len, value1) { + value2 = json_object_getn(object2, key, key_len); + + if (!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_object_copy(json_t *object) { + json_t *result; + + const char *key; + size_t key_len; + json_t *value; + + result = json_object(); + if (!result) + return NULL; + + json_object_keylen_foreach(object, key, key_len, value) + json_object_setn_nocheck(result, key, key_len, value); + + return result; +} + +static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents) { + json_t *result; + void *iter; + char loop_key[LOOP_KEY_LEN]; + size_t loop_key_len; + + if (jsonp_loop_check(parents, object, loop_key, sizeof(loop_key), &loop_key_len)) + return NULL; + + result = json_object(); + if (!result) + goto out; + + /* Cannot use json_object_foreach because object has to be cast + non-const */ + iter = json_object_iter((json_t *)object); + while (iter) { + const char *key; + size_t key_len; + const json_t *value; + key = json_object_iter_key(iter); + key_len = json_object_iter_key_len(iter); + value = json_object_iter_value(iter); + + if (json_object_setn_new_nocheck(result, key, key_len, + do_deep_copy(value, parents))) { + json_decref(result); + result = NULL; + break; + } + iter = json_object_iter_next((json_t *)object, iter); + } + +out: + hashtable_del(parents, loop_key, loop_key_len); + + return result; +} + +/*** array ***/ + +json_t *json_array(void) { + json_array_t *array = jsonp_malloc(sizeof(json_array_t)); + if (!array) + return NULL; + json_init(&array->json, JSON_ARRAY); + + array->entries = 0; + array->size = 8; + + array->table = jsonp_malloc(array->size * sizeof(json_t *)); + if (!array->table) { + jsonp_free(array); + return NULL; + } + + return &array->json; +} + +static void json_delete_array(json_array_t *array) { + size_t i; + + for (i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + jsonp_free(array->table); + jsonp_free(array); +} + +size_t json_array_size(const json_t *json) { + if (!json_is_array(json)) + return 0; + + return json_to_array(json)->entries; +} + +json_t *json_array_get(const json_t *json, size_t index) { + json_array_t *array; + if (!json_is_array(json)) + return NULL; + array = json_to_array(json); + + if (index >= array->entries) + return NULL; + + return array->table[index]; +} + +int json_array_set_new(json_t *json, size_t index, json_t *value) { + json_array_t *array; + + if (!value) + return -1; + + if (!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if (index >= array->entries) { + json_decref(value); + return -1; + } + + json_decref(array->table[index]); + array->table[index] = value; + + return 0; +} + +static void array_move(json_array_t *array, size_t dest, size_t src, size_t count) { + memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *)); +} + +static void array_copy(json_t **dest, size_t dpos, json_t **src, size_t spos, + size_t count) { + memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *)); +} + +static json_t **json_array_grow(json_array_t *array, size_t amount, int copy) { + size_t new_size; + json_t **old_table, **new_table; + + if (array->entries + amount <= array->size) + return array->table; + + old_table = array->table; + + new_size = max(array->size + amount, array->size * 2); + new_table = jsonp_malloc(new_size * sizeof(json_t *)); + if (!new_table) + return NULL; + + array->size = new_size; + array->table = new_table; + + if (copy) { + array_copy(array->table, 0, old_table, 0, array->entries); + jsonp_free(old_table); + return array->table; + } + + return old_table; +} + +int json_array_append_new(json_t *json, json_t *value) { + json_array_t *array; + + if (!value) + return -1; + + if (!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if (!json_array_grow(array, 1, 1)) { + json_decref(value); + return -1; + } + + array->table[array->entries] = value; + array->entries++; + + return 0; +} + +int json_array_insert_new(json_t *json, size_t index, json_t *value) { + json_array_t *array; + json_t **old_table; + + if (!value) + return -1; + + if (!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if (index > array->entries) { + json_decref(value); + return -1; + } + + old_table = json_array_grow(array, 1, 0); + if (!old_table) { + json_decref(value); + return -1; + } + + if (old_table != array->table) { + array_copy(array->table, 0, old_table, 0, index); + array_copy(array->table, index + 1, old_table, index, array->entries - index); + jsonp_free(old_table); + } else + array_move(array, index + 1, index, array->entries - index); + + array->table[index] = value; + array->entries++; + + return 0; +} + +int json_array_remove(json_t *json, size_t index) { + json_array_t *array; + + if (!json_is_array(json)) + return -1; + array = json_to_array(json); + + if (index >= array->entries) + return -1; + + json_decref(array->table[index]); + + /* If we're removing the last element, nothing has to be moved */ + if (index < array->entries - 1) + array_move(array, index, index + 1, array->entries - index - 1); + + array->entries--; + + return 0; +} + +int json_array_clear(json_t *json) { + json_array_t *array; + size_t i; + + if (!json_is_array(json)) + return -1; + array = json_to_array(json); + + for (i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + array->entries = 0; + return 0; +} + +int json_array_extend(json_t *json, json_t *other_json) { + json_array_t *array, *other; + size_t i; + + if (!json_is_array(json) || !json_is_array(other_json)) + return -1; + array = json_to_array(json); + other = json_to_array(other_json); + + if (!json_array_grow(array, other->entries, 1)) + return -1; + + for (i = 0; i < other->entries; i++) + json_incref(other->table[i]); + + array_copy(array->table, array->entries, other->table, 0, other->entries); + + array->entries += other->entries; + return 0; +} + +static int json_array_equal(const json_t *array1, const json_t *array2) { + size_t i, size; + + size = json_array_size(array1); + if (size != json_array_size(array2)) + return 0; + + for (i = 0; i < size; i++) { + json_t *value1, *value2; + + value1 = json_array_get(array1, i); + value2 = json_array_get(array2, i); + + if (!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_array_copy(json_t *array) { + json_t *result; + size_t i; + + result = json_array(); + if (!result) + return NULL; + + for (i = 0; i < json_array_size(array); i++) + json_array_append(result, json_array_get(array, i)); + + return result; +} + +static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents) { + json_t *result; + size_t i; + char loop_key[LOOP_KEY_LEN]; + size_t loop_key_len; + + if (jsonp_loop_check(parents, array, loop_key, sizeof(loop_key), &loop_key_len)) + return NULL; + + result = json_array(); + if (!result) + goto out; + + for (i = 0; i < json_array_size(array); i++) { + if (json_array_append_new(result, + do_deep_copy(json_array_get(array, i), parents))) { + json_decref(result); + result = NULL; + break; + } + } + +out: + hashtable_del(parents, loop_key, loop_key_len); + + return result; +} + +/*** string ***/ + +static json_t *string_create(const char *value, size_t len, int own) { + char *v; + json_string_t *string; + + if (!value) + return NULL; + + if (own) + v = (char *)value; + else { + v = jsonp_strndup(value, len); + if (!v) + return NULL; + } + + string = jsonp_malloc(sizeof(json_string_t)); + if (!string) { + jsonp_free(v); + return NULL; + } + json_init(&string->json, JSON_STRING); + string->value = v; + string->length = len; + + return &string->json; +} + +json_t *json_string_nocheck(const char *value) { + if (!value) + return NULL; + + return string_create(value, strlen(value), 0); +} + +json_t *json_stringn_nocheck(const char *value, size_t len) { + return string_create(value, len, 0); +} + +/* this is private; "steal" is not a public API concept */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len) { + return string_create(value, len, 1); +} + +json_t *json_string(const char *value) { + if (!value) + return NULL; + + return json_stringn(value, strlen(value)); +} + +json_t *json_stringn(const char *value, size_t len) { + if (!value || !utf8_check_string(value, len)) + return NULL; + + return json_stringn_nocheck(value, len); +} + +const char *json_string_value(const json_t *json) { + if (!json_is_string(json)) + return NULL; + + return json_to_string(json)->value; +} + +size_t json_string_length(const json_t *json) { + if (!json_is_string(json)) + return 0; + + return json_to_string(json)->length; +} + +int json_string_set_nocheck(json_t *json, const char *value) { + if (!value) + return -1; + + return json_string_setn_nocheck(json, value, strlen(value)); +} + +int json_string_setn_nocheck(json_t *json, const char *value, size_t len) { + char *dup; + json_string_t *string; + + if (!json_is_string(json) || !value) + return -1; + + dup = jsonp_strndup(value, len); + if (!dup) + return -1; + + string = json_to_string(json); + jsonp_free(string->value); + string->value = dup; + string->length = len; + + return 0; +} + +int json_string_set(json_t *json, const char *value) { + if (!value) + return -1; + + return json_string_setn(json, value, strlen(value)); +} + +int json_string_setn(json_t *json, const char *value, size_t len) { + if (!value || !utf8_check_string(value, len)) + return -1; + + return json_string_setn_nocheck(json, value, len); +} + +static void json_delete_string(json_string_t *string) { + jsonp_free(string->value); + jsonp_free(string); +} + +static int json_string_equal(const json_t *string1, const json_t *string2) { + json_string_t *s1, *s2; + + s1 = json_to_string(string1); + s2 = json_to_string(string2); + return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length); +} + +static json_t *json_string_copy(const json_t *string) { + json_string_t *s; + + s = json_to_string(string); + return json_stringn_nocheck(s->value, s->length); +} + +json_t *json_vsprintf(const char *fmt, va_list ap) { + json_t *json = NULL; + int length; + char *buf; + va_list aq; + va_copy(aq, ap); + + length = vsnprintf(NULL, 0, fmt, ap); + if (length < 0) + goto out; + if (length == 0) { + json = json_string(""); + goto out; + } + + buf = jsonp_malloc((size_t)length + 1); + if (!buf) + goto out; + + vsnprintf(buf, (size_t)length + 1, fmt, aq); + if (!utf8_check_string(buf, length)) { + jsonp_free(buf); + goto out; + } + + json = jsonp_stringn_nocheck_own(buf, length); + +out: + va_end(aq); + return json; +} + +json_t *json_sprintf(const char *fmt, ...) { + json_t *result; + va_list ap; + + va_start(ap, fmt); + result = json_vsprintf(fmt, ap); + va_end(ap); + + return result; +} + +/*** integer ***/ + +json_t *json_integer(json_int_t value) { + json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t)); + if (!integer) + return NULL; + json_init(&integer->json, JSON_INTEGER); + + integer->value = value; + return &integer->json; +} + +json_int_t json_integer_value(const json_t *json) { + if (!json_is_integer(json)) + return 0; + + return json_to_integer(json)->value; +} + +int json_integer_set(json_t *json, json_int_t value) { + if (!json_is_integer(json)) + return -1; + + json_to_integer(json)->value = value; + + return 0; +} + +static void json_delete_integer(json_integer_t *integer) { jsonp_free(integer); } + +static int json_integer_equal(const json_t *integer1, const json_t *integer2) { + return json_integer_value(integer1) == json_integer_value(integer2); +} + +static json_t *json_integer_copy(const json_t *integer) { + return json_integer(json_integer_value(integer)); +} + +/*** real ***/ + +json_t *json_real(double value) { + json_real_t *real; + + if (isnan(value) || isinf(value)) + return NULL; + + real = jsonp_malloc(sizeof(json_real_t)); + if (!real) + return NULL; + json_init(&real->json, JSON_REAL); + + real->value = value; + return &real->json; +} + +double json_real_value(const json_t *json) { + if (!json_is_real(json)) + return 0; + + return json_to_real(json)->value; +} + +int json_real_set(json_t *json, double value) { + if (!json_is_real(json) || isnan(value) || isinf(value)) + return -1; + + json_to_real(json)->value = value; + + return 0; +} + +static void json_delete_real(json_real_t *real) { jsonp_free(real); } + +static int json_real_equal(const json_t *real1, const json_t *real2) { + return json_real_value(real1) == json_real_value(real2); +} + +static json_t *json_real_copy(const json_t *real) { + return json_real(json_real_value(real)); +} + +/*** number ***/ + +double json_number_value(const json_t *json) { + if (json_is_integer(json)) + return (double)json_integer_value(json); + else if (json_is_real(json)) + return json_real_value(json); + else + return 0.0; +} + +/*** simple values ***/ + +json_t *json_true(void) { + static json_t the_true = {JSON_TRUE, (size_t)-1}; + return &the_true; +} + +json_t *json_false(void) { + static json_t the_false = {JSON_FALSE, (size_t)-1}; + return &the_false; +} + +json_t *json_null(void) { + static json_t the_null = {JSON_NULL, (size_t)-1}; + return &the_null; +} + +/*** deletion ***/ + +void json_delete(json_t *json) { + if (!json) + return; + + switch (json_typeof(json)) { + case JSON_OBJECT: + json_delete_object(json_to_object(json)); + break; + case JSON_ARRAY: + json_delete_array(json_to_array(json)); + break; + case JSON_STRING: + json_delete_string(json_to_string(json)); + break; + case JSON_INTEGER: + json_delete_integer(json_to_integer(json)); + break; + case JSON_REAL: + json_delete_real(json_to_real(json)); + break; + default: + return; + } + + /* json_delete is not called for true, false or null */ +} + +/*** equality ***/ + +int json_equal(const json_t *json1, const json_t *json2) { + if (!json1 || !json2) + return 0; + + if (json_typeof(json1) != json_typeof(json2)) + return 0; + + /* this covers true, false and null as they are singletons */ + if (json1 == json2) + return 1; + + switch (json_typeof(json1)) { + case JSON_OBJECT: + return json_object_equal(json1, json2); + case JSON_ARRAY: + return json_array_equal(json1, json2); + case JSON_STRING: + return json_string_equal(json1, json2); + case JSON_INTEGER: + return json_integer_equal(json1, json2); + case JSON_REAL: + return json_real_equal(json1, json2); + default: + return 0; + } +} + +/*** copying ***/ + +json_t *json_copy(json_t *json) { + if (!json) + return NULL; + + switch (json_typeof(json)) { + case JSON_OBJECT: + return json_object_copy(json); + case JSON_ARRAY: + return json_array_copy(json); + case JSON_STRING: + return json_string_copy(json); + case JSON_INTEGER: + return json_integer_copy(json); + case JSON_REAL: + return json_real_copy(json); + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + return json; + default: + return NULL; + } +} + +json_t *json_deep_copy(const json_t *json) { + json_t *res; + hashtable_t parents_set; + + if (hashtable_init(&parents_set)) + return NULL; + res = do_deep_copy(json, &parents_set); + hashtable_close(&parents_set); + + return res; +} + +json_t *do_deep_copy(const json_t *json, hashtable_t *parents) { + if (!json) + return NULL; + + switch (json_typeof(json)) { + case JSON_OBJECT: + return json_object_deep_copy(json, parents); + case JSON_ARRAY: + return json_array_deep_copy(json, parents); + /* for the rest of the types, deep copying doesn't differ from + shallow copying */ + case JSON_STRING: + return json_string_copy(json); + case JSON_INTEGER: + return json_integer_copy(json); + case JSON_REAL: + return json_real_copy(json); + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + return (json_t *)json; + default: + return NULL; + } +} diff --git a/lib/jansson/src/version.c b/lib/jansson/src/version.c new file mode 100644 index 0000000..f1026af --- /dev/null +++ b/lib/jansson/src/version.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Sean Bright + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson.h" + +const char *jansson_version_str(void) { return JANSSON_VERSION; } + +int jansson_version_cmp(int major, int minor, int micro) { + int diff; + + if ((diff = JANSSON_MAJOR_VERSION - major)) { + return diff; + } + + if ((diff = JANSSON_MINOR_VERSION - minor)) { + return diff; + } + + return JANSSON_MICRO_VERSION - micro; +} diff --git a/lib/jansson/test/.gitignore b/lib/jansson/test/.gitignore new file mode 100644 index 0000000..93cc8da --- /dev/null +++ b/lib/jansson/test/.gitignore @@ -0,0 +1,21 @@ +logs +bin/json_process +suites/api/test_array +suites/api/test_chaos +suites/api/test_copy +suites/api/test_cpp +suites/api/test_dump +suites/api/test_dump_callback +suites/api/test_equal +suites/api/test_fixed_size +suites/api/test_load +suites/api/test_load_callback +suites/api/test_loadb +suites/api/test_memory_funcs +suites/api/test_number +suites/api/test_object +suites/api/test_pack +suites/api/test_simple +suites/api/test_sprintf +suites/api/test_unpack +suites/api/test_version diff --git a/lib/jansson/test/Makefile.am b/lib/jansson/test/Makefile.am new file mode 100644 index 0000000..344d18d --- /dev/null +++ b/lib/jansson/test/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = bin suites ossfuzz +EXTRA_DIST = scripts run-suites + +TESTS = run-suites +TESTS_ENVIRONMENT = \ + top_srcdir=$(top_srcdir) \ + top_builddir=$(top_builddir) + +clean-local: + rm -rf logs diff --git a/lib/jansson/test/bin/Makefile.am b/lib/jansson/test/bin/Makefile.am new file mode 100644 index 0000000..63b6dce --- /dev/null +++ b/lib/jansson/test/bin/Makefile.am @@ -0,0 +1,5 @@ +check_PROGRAMS = json_process + +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src +LDFLAGS = -static # for speed and Valgrind +LDADD = $(top_builddir)/src/libjansson.la diff --git a/lib/jansson/test/bin/json_process.c b/lib/jansson/test/bin/json_process.c new file mode 100644 index 0000000..965fa6b --- /dev/null +++ b/lib/jansson/test/bin/json_process.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_LOCALE_H +#include +#endif + +#if _WIN32 +#include /* for _O_BINARY */ +#include /* for _setmode() */ + +static const char dir_sep = '\\'; +#else +static const char dir_sep = '/'; +#endif + +struct config { + int indent; + int compact; + int preserve_order; + int ensure_ascii; + int sort_keys; + int strip; + int have_hashseed; + int hashseed; + int precision; +} conf; + +#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t') + +/* Return a pointer to the first non-whitespace character of str. + Modifies str so that all trailing whitespace characters are + replaced by '\0'. */ +static const char *strip(char *str) { + size_t length; + char *result = str; + while (*result && l_isspace(*result)) + result++; + + length = strlen(result); + if (length == 0) + return result; + + while (l_isspace(result[length - 1])) + result[--length] = '\0'; + + return result; +} + +static char *loadfile(FILE *file) { + size_t fsize, ret; + char *buf; + + fseek(file, 0, SEEK_END); + fsize = ftell(file); + fseek(file, 0, SEEK_SET); + + buf = malloc(fsize + 1); + ret = fread(buf, 1, fsize, file); + if (ret != fsize) + exit(1); + buf[fsize] = '\0'; + + return buf; +} + +static void read_conf(FILE *conffile) { + char *buffer, *line, *val; + conf.have_hashseed = 0; + + buffer = loadfile(conffile); + for (line = strtok(buffer, "\r\n"); line; line = strtok(NULL, "\r\n")) { + val = strchr(line, '='); + if (!val) { + printf("invalid configuration line\n"); + break; + } + *val++ = '\0'; + + if (!strcmp(line, "JSON_INDENT")) + conf.indent = atoi(val); + if (!strcmp(line, "JSON_COMPACT")) + conf.compact = atoi(val); + if (!strcmp(line, "JSON_ENSURE_ASCII")) + conf.ensure_ascii = atoi(val); + if (!strcmp(line, "JSON_PRESERVE_ORDER")) + conf.preserve_order = atoi(val); + if (!strcmp(line, "JSON_SORT_KEYS")) + conf.sort_keys = atoi(val); + if (!strcmp(line, "JSON_REAL_PRECISION")) + conf.precision = atoi(val); + if (!strcmp(line, "STRIP")) + conf.strip = atoi(val); + if (!strcmp(line, "HASHSEED")) { + conf.have_hashseed = 1; + conf.hashseed = atoi(val); + } + } + + free(buffer); +} + +static int cmpfile(const char *str, const char *path, const char *fname) { + char filename[1024], *buffer; + int ret; + FILE *file; + + sprintf(filename, "%s%c%s", path, dir_sep, fname); + file = fopen(filename, "rb"); + if (!file) { + if (conf.strip) + strcat(filename, ".strip"); + else + strcat(filename, ".normal"); + file = fopen(filename, "rb"); + } + if (!file) { + printf("Error: test result file could not be opened.\n"); + exit(1); + } + + buffer = loadfile(file); + if (strcmp(buffer, str) != 0) { + fprintf(stderr, "=== Expected %s ===\n", fname); + fprintf(stderr, "%s\n", buffer); + fprintf(stderr, "=== Actual %s ===\n", fname); + fprintf(stderr, "%s\n", str); + ret = 1; + } else { + ret = 0; + } + + free(buffer); + fclose(file); + + return ret; +} + +int use_conf(char *test_path) { + int ret; + size_t flags = 0; + char filename[1024], errstr[1024]; + char *buffer; + FILE *infile, *conffile; + json_t *json; + json_error_t error; + + sprintf(filename, "%s%cinput", test_path, dir_sep); + if (!(infile = fopen(filename, "rb"))) { + fprintf(stderr, "Could not open \"%s\"\n", filename); + return 2; + } + + sprintf(filename, "%s%cenv", test_path, dir_sep); + conffile = fopen(filename, "rb"); + if (conffile) { + read_conf(conffile); + fclose(conffile); + } + + if (conf.indent < 0 || conf.indent > 31) { + fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent); + fclose(infile); + return 2; + } + if (conf.indent) + flags |= JSON_INDENT(conf.indent); + + if (conf.compact) + flags |= JSON_COMPACT; + + if (conf.ensure_ascii) + flags |= JSON_ENSURE_ASCII; + + if (conf.preserve_order) + flags |= JSON_PRESERVE_ORDER; + + if (conf.sort_keys) + flags |= JSON_SORT_KEYS; + + if (conf.precision < 0 || conf.precision > 31) { + fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n", conf.precision); + fclose(infile); + return 2; + } + if (conf.precision) + flags |= JSON_REAL_PRECISION(conf.precision); + + if (conf.have_hashseed) + json_object_seed(conf.hashseed); + + if (conf.strip) { + /* Load to memory, strip leading and trailing whitespace */ + buffer = loadfile(infile); + json = json_loads(strip(buffer), 0, &error); + free(buffer); + } else { + json = json_loadf(infile, 0, &error); + } + + fclose(infile); + + if (!json) { + sprintf(errstr, "%d %d %d\n%s\n", error.line, error.column, error.position, + error.text); + + ret = cmpfile(errstr, test_path, "error"); + return ret; + } + + buffer = json_dumps(json, flags); + ret = cmpfile(buffer, test_path, "output"); + free(buffer); + json_decref(json); + + return ret; +} + +int main(int argc, char *argv[]) { + int i; + char *test_path = NULL; + +#ifdef HAVE_SETLOCALE + setlocale(LC_ALL, ""); +#endif + + if (argc < 2) { + goto usage; + } + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--strip")) + conf.strip = 1; + else + test_path = argv[i]; + } + + if (!test_path) { + goto usage; + } + + return use_conf(test_path); + +usage: + fprintf(stderr, "usage: %s [--strip] test_dir\n", argv[0]); + return 2; +} diff --git a/lib/jansson/test/ossfuzz/.gitignore b/lib/jansson/test/ossfuzz/.gitignore new file mode 100644 index 0000000..7fbb867 --- /dev/null +++ b/lib/jansson/test/ossfuzz/.gitignore @@ -0,0 +1 @@ +json_load_dump_fuzzer diff --git a/lib/jansson/test/ossfuzz/Makefile.am b/lib/jansson/test/ossfuzz/Makefile.am new file mode 100644 index 0000000..a2e802e --- /dev/null +++ b/lib/jansson/test/ossfuzz/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src +LDADD = $(top_builddir)/src/libjansson.la + +if USE_OSSFUZZ_FLAG +FUZZ_FLAG = $(LIB_FUZZING_ENGINE) +else +if USE_OSSFUZZ_STATIC +LDADD += $(LIB_FUZZING_ENGINE) +FUZZ_FLAG = +else +LDADD += libstandaloneengine.a +FUZZ_FLAG = +endif +endif + +noinst_PROGRAMS = +noinst_LIBRARIES = + +if USE_OSSFUZZERS +noinst_PROGRAMS += \ + json_load_dump_fuzzer + +noinst_LIBRARIES += \ + libstandaloneengine.a +endif + +json_load_dump_fuzzer_SOURCES = json_load_dump_fuzzer.cc testinput.h +json_load_dump_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +json_load_dump_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static + +libstandaloneengine_a_SOURCES = standaloneengine.cc +libstandaloneengine_a_CXXFLAGS = $(AM_CXXFLAGS) diff --git a/lib/jansson/test/ossfuzz/json_load_dump_fuzzer.cc b/lib/jansson/test/ossfuzz/json_load_dump_fuzzer.cc new file mode 100644 index 0000000..bc3844e --- /dev/null +++ b/lib/jansson/test/ossfuzz/json_load_dump_fuzzer.cc @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#include "jansson.h" + +static int enable_diags; + +#define FUZZ_DEBUG(FMT, ...) \ + if (enable_diags) \ + { \ + fprintf(stderr, FMT, ##__VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } + + +static int json_dump_counter(const char *buffer, size_t size, void *data) +{ + uint64_t *counter = reinterpret_cast(data); + *counter += size; + return 0; +} + + +#define NUM_COMMAND_BYTES (sizeof(size_t) + sizeof(size_t) + 1) + +#define FUZZ_DUMP_CALLBACK 0x00 +#define FUZZ_DUMP_STRING 0x01 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + json_error_t error; + unsigned char dump_mode; + + // Enable or disable diagnostics based on the FUZZ_VERBOSE environment flag. + enable_diags = (getenv("FUZZ_VERBOSE") != NULL); + + FUZZ_DEBUG("Input data length: %zd", size); + + if (size < NUM_COMMAND_BYTES) + { + return 0; + } + + // Use the first sizeof(size_t) bytes as load flags. + size_t load_flags = *(const size_t*)data; + data += sizeof(size_t); + + FUZZ_DEBUG("load_flags: 0x%zx\n" + "& JSON_REJECT_DUPLICATES = 0x%zx\n" + "& JSON_DECODE_ANY = 0x%zx\n" + "& JSON_DISABLE_EOF_CHECK = 0x%zx\n" + "& JSON_DECODE_INT_AS_REAL = 0x%zx\n" + "& JSON_ALLOW_NUL = 0x%zx\n", + load_flags, + load_flags & JSON_REJECT_DUPLICATES, + load_flags & JSON_DECODE_ANY, + load_flags & JSON_DISABLE_EOF_CHECK, + load_flags & JSON_DECODE_INT_AS_REAL, + load_flags & JSON_ALLOW_NUL); + + // Use the next sizeof(size_t) bytes as dump flags. + size_t dump_flags = *(const size_t*)data; + data += sizeof(size_t); + + FUZZ_DEBUG("dump_flags: 0x%zx\n" + "& JSON_MAX_INDENT = 0x%zx\n" + "& JSON_COMPACT = 0x%zx\n" + "& JSON_ENSURE_ASCII = 0x%zx\n" + "& JSON_SORT_KEYS = 0x%zx\n" + "& JSON_PRESERVE_ORDER = 0x%zx\n" + "& JSON_ENCODE_ANY = 0x%zx\n" + "& JSON_ESCAPE_SLASH = 0x%zx\n" + "& JSON_REAL_PRECISION = 0x%zx\n" + "& JSON_EMBED = 0x%zx\n", + dump_flags, + dump_flags & JSON_MAX_INDENT, + dump_flags & JSON_COMPACT, + dump_flags & JSON_ENSURE_ASCII, + dump_flags & JSON_SORT_KEYS, + dump_flags & JSON_PRESERVE_ORDER, + dump_flags & JSON_ENCODE_ANY, + dump_flags & JSON_ESCAPE_SLASH, + ((dump_flags >> 11) & 0x1F) << 11, + dump_flags & JSON_EMBED); + + // Use the next byte as the dump mode. + dump_mode = data[0]; + data++; + + FUZZ_DEBUG("dump_mode: 0x%x", (unsigned int)dump_mode); + + // Remove the command bytes from the size total. + size -= NUM_COMMAND_BYTES; + + // Attempt to load the remainder of the data with the given load flags. + const char* text = reinterpret_cast(data); + json_t* jobj = json_loadb(text, size, load_flags, &error); + + if (jobj == NULL) + { + return 0; + } + + if (dump_mode & FUZZ_DUMP_STRING) + { + // Dump as a string. Remove indents so that we don't run out of memory. + char *out = json_dumps(jobj, dump_flags & ~JSON_MAX_INDENT); + if (out != NULL) + { + free(out); + } + } + else + { + // Default is callback mode. + // + // Attempt to dump the loaded json object with the given dump flags. + uint64_t counter = 0; + + json_dump_callback(jobj, json_dump_counter, &counter, dump_flags); + FUZZ_DEBUG("Counter function counted %" PRIu64 " bytes.", counter); + } + + if (jobj) + { + json_decref(jobj); + } + + return 0; +} diff --git a/lib/jansson/test/ossfuzz/ossfuzz.sh b/lib/jansson/test/ossfuzz/ossfuzz.sh new file mode 100755 index 0000000..59740c2 --- /dev/null +++ b/lib/jansson/test/ossfuzz/ossfuzz.sh @@ -0,0 +1,30 @@ +#!/bin/bash -eu + +# This script is called by the oss-fuzz main project when compiling the fuzz +# targets. This script is regression tested by travisoss.sh. + +# Save off the current folder as the build root. +export BUILD_ROOT=$PWD + +echo "CC: $CC" +echo "CXX: $CXX" +echo "LIB_FUZZING_ENGINE: $LIB_FUZZING_ENGINE" +echo "CFLAGS: $CFLAGS" +echo "CXXFLAGS: $CXXFLAGS" +echo "OUT: $OUT" + +export MAKEFLAGS+="-j$(nproc)" + +# Install dependencies +apt-get -y install automake libtool + +# Compile the fuzzer. +autoreconf -i +./configure --enable-ossfuzzers +make + +# Copy the fuzzer to the output directory. +cp -v test/ossfuzz/json_load_dump_fuzzer $OUT/ + +# Zip up all input files to use as a test corpus +find test/suites -name "input" -print | zip $OUT/json_load_dump_fuzzer_seed_corpus.zip -@ diff --git a/lib/jansson/test/ossfuzz/standaloneengine.cc b/lib/jansson/test/ossfuzz/standaloneengine.cc new file mode 100644 index 0000000..175360e --- /dev/null +++ b/lib/jansson/test/ossfuzz/standaloneengine.cc @@ -0,0 +1,74 @@ +#include +#include +#include + +#include "testinput.h" + +/** + * Main procedure for standalone fuzzing engine. + * + * Reads filenames from the argument array. For each filename, read the file + * into memory and then call the fuzzing interface with the data. + */ +int main(int argc, char **argv) +{ + int ii; + for(ii = 1; ii < argc; ii++) + { + FILE *infile; + printf("[%s] ", argv[ii]); + + /* Try and open the file. */ + infile = fopen(argv[ii], "rb"); + if(infile) + { + uint8_t *buffer = NULL; + size_t buffer_len; + + printf("Opened.. "); + + /* Get the length of the file. */ + fseek(infile, 0L, SEEK_END); + buffer_len = ftell(infile); + + /* Reset the file indicator to the beginning of the file. */ + fseek(infile, 0L, SEEK_SET); + + /* Allocate a buffer for the file contents. */ + buffer = (uint8_t *)calloc(buffer_len, sizeof(uint8_t)); + if(buffer) + { + /* Read all the text from the file into the buffer. */ + fread(buffer, sizeof(uint8_t), buffer_len, infile); + printf("Read %zu bytes, fuzzing.. ", buffer_len); + + /* Call the fuzzer with the data. */ + LLVMFuzzerTestOneInput(buffer, buffer_len); + + printf("complete !!"); + + /* Free the buffer as it's no longer needed. */ + free(buffer); + buffer = NULL; + } + else + { + fprintf(stderr, + "[%s] Failed to allocate %zu bytes \n", + argv[ii], + buffer_len); + } + + /* Close the file as it's no longer needed. */ + fclose(infile); + infile = NULL; + } + else + { + /* Failed to open the file. Maybe wrong name or wrong permissions? */ + fprintf(stderr, "[%s] Open failed. \n", argv[ii]); + } + + printf("\n"); + } +} diff --git a/lib/jansson/test/ossfuzz/testinput.h b/lib/jansson/test/ossfuzz/testinput.h new file mode 100644 index 0000000..6ab9b51 --- /dev/null +++ b/lib/jansson/test/ossfuzz/testinput.h @@ -0,0 +1,3 @@ +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); diff --git a/lib/jansson/test/run-suites b/lib/jansson/test/run-suites new file mode 100755 index 0000000..fd1b89b --- /dev/null +++ b/lib/jansson/test/run-suites @@ -0,0 +1,50 @@ +#!/bin/sh + +while [ -n "$1" ]; do + suite=$1 + if [ -x $top_srcdir/test/suites/$suite/run ]; then + SUITES="$SUITES $suite" + else + echo "No such suite: $suite" + exit 1 + fi + shift +done + +if [ -z "$SUITES" ]; then + suitedirs=$top_srcdir/test/suites/* + for suitedir in $suitedirs; do + if [ -d $suitedir ]; then + SUITES="$SUITES `basename $suitedir`" + fi + done +fi + +[ -z "$STOP" ] && STOP=0 + +suites_srcdir=$top_srcdir/test/suites +suites_builddir=suites +scriptdir=$top_srcdir/test/scripts +logdir=logs +bindir=bin +export suites_srcdir suites_builddir scriptdir logdir bindir + +passed=0 +failed=0 +for suite in $SUITES; do + echo "Suite: $suite" + if $suites_srcdir/$suite/run $suite; then + passed=`expr $passed + 1` + else + failed=`expr $failed + 1` + [ $STOP -eq 1 ] && break + fi +done + +if [ $failed -gt 0 ]; then + echo "$failed of `expr $passed + $failed` test suites failed" + exit 1 +else + echo "$passed test suites passed" + rm -rf $logdir +fi diff --git a/lib/jansson/test/scripts/run-tests.sh b/lib/jansson/test/scripts/run-tests.sh new file mode 100644 index 0000000..f980a76 --- /dev/null +++ b/lib/jansson/test/scripts/run-tests.sh @@ -0,0 +1,100 @@ +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +die() { + echo "$1" >&2 + exit 1 +} + +[ -n "$1" ] || die "Usage: $0 suite-name" +[ -n "$bindir" ] || die "Set bindir" +[ -n "$logdir" ] || die "Set logdir" +[ -n "$scriptdir" ] || die "Set scriptdir" +[ -n "$suites_srcdir" ] || die "Set suites_srcdir" +[ -n "$suites_builddir" ] || die "Set suites_builddir" + +json_process=$bindir/json_process + +suite_name=$1 +suite_srcdir=$suites_srcdir/$suite_name +suite_builddir=$suites_builddir/$suite_name +suite_log=$logdir/$suite_name + +[ -z "$VERBOSE" ] && VERBOSE=0 +[ -z "$STOP" ] && STOP=0 + +. $scriptdir/valgrind.sh + +rm -rf $suite_log +mkdir -p $suite_log + +for test_path in $suite_srcdir/*; do + test_name=$(basename $test_path) + test_builddir=$suite_builddir/$test_name + test_log=$suite_log/$test_name + + [ "$test_name" = "run" ] && continue + is_test || continue + + rm -rf $test_log + mkdir -p $test_log + if [ $VERBOSE -eq 1 ]; then + printf '%s... ' "$test_name" + fi + + run_test + case $? in + 0) + # Success + if [ $VERBOSE -eq 1 ]; then + printf 'ok\n' + else + printf '.' + fi + rm -rf $test_log + ;; + + 77) + # Skip + if [ $VERBOSE -eq 1 ]; then + printf 'skipped\n' + else + printf 'S' + fi + rm -rf $test_log + ;; + + *) + # Failure + if [ $VERBOSE -eq 1 ]; then + printf 'FAILED\n' + else + printf 'F' + fi + + [ $STOP -eq 1 ] && break + ;; + esac +done + +if [ $VERBOSE -eq 0 ]; then + printf '\n' +fi + +if [ -n "$(ls -A $suite_log)" ]; then + for test_log in $suite_log/*; do + test_name=$(basename $test_log) + test_path=$suite_srcdir/$test_name + echo "=================================================================" + echo "$suite_name/$test_name" + echo "=================================================================" + show_error + echo + done + echo "=================================================================" + exit 1 +else + rm -rf $suite_log +fi diff --git a/lib/jansson/test/scripts/valgrind.sh b/lib/jansson/test/scripts/valgrind.sh new file mode 100644 index 0000000..afbdcee --- /dev/null +++ b/lib/jansson/test/scripts/valgrind.sh @@ -0,0 +1,35 @@ +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +[ -z "$VALGRIND" ] && VALGRIND=0 + +VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q" + +if [ $VALGRIND -eq 1 ]; then + test_runner="$VALGRIND_CMDLINE" + json_process="$VALGRIND_CMDLINE $json_process" +else + test_runner="" +fi + +valgrind_check() { + if [ $VALGRIND -eq 1 ]; then + # Check for Valgrind error output. The valgrind option + # --error-exitcode is not enough because Valgrind doesn't + # think unfreed allocs are errors. + if grep -E -q '^==[0-9]+== ' $1; then + touch $test_log/valgrind_error + return 1 + fi + fi +} + +valgrind_show_error() { + if [ $VALGRIND -eq 1 -a -f $test_log/valgrind_error ]; then + echo "valgrind detected an error" + return 0 + fi + return 1 +} diff --git a/lib/jansson/test/suites/.gitattributes b/lib/jansson/test/suites/.gitattributes new file mode 100644 index 0000000..68d8861 --- /dev/null +++ b/lib/jansson/test/suites/.gitattributes @@ -0,0 +1,2 @@ +api/ text=auto +* text eol=lf \ No newline at end of file diff --git a/lib/jansson/test/suites/Makefile.am b/lib/jansson/test/suites/Makefile.am new file mode 100644 index 0000000..a53eb07 --- /dev/null +++ b/lib/jansson/test/suites/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = api +EXTRA_DIST = invalid invalid-unicode valid diff --git a/lib/jansson/test/suites/api/Makefile.am b/lib/jansson/test/suites/api/Makefile.am new file mode 100644 index 0000000..2bc638b --- /dev/null +++ b/lib/jansson/test/suites/api/Makefile.am @@ -0,0 +1,42 @@ +EXTRA_DIST = run check-exports + +check_PROGRAMS = \ + test_array \ + test_chaos \ + test_copy \ + test_dump \ + test_dump_callback \ + test_equal \ + test_fixed_size \ + test_load \ + test_load_callback \ + test_loadb \ + test_memory_funcs \ + test_number \ + test_object \ + test_pack \ + test_simple \ + test_sprintf \ + test_unpack \ + test_version + +test_array_SOURCES = test_array.c util.h +test_chaos_SOURCES = test_chaos.c util.h +test_copy_SOURCES = test_copy.c util.h +test_dump_SOURCES = test_dump.c util.h +test_dump_callback_SOURCES = test_dump_callback.c util.h +test_fixed_size_SOURCES = test_fixed_size.c util.h +test_load_SOURCES = test_load.c util.h +test_loadb_SOURCES = test_loadb.c util.h +test_memory_funcs_SOURCES = test_memory_funcs.c util.h +test_number_SOURCES = test_number.c util.h +test_object_SOURCES = test_object.c util.h +test_pack_SOURCES = test_pack.c util.h +test_simple_SOURCES = test_simple.c util.h +test_sprintf_SOURCES = test_sprintf.c util.h +test_unpack_SOURCES = test_unpack.c util.h +test_version_SOURCES = test_version.c util.h + +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src +LDFLAGS = -static # for speed and Valgrind +LDADD = $(top_builddir)/src/libjansson.la diff --git a/lib/jansson/test/suites/api/check-exports b/lib/jansson/test/suites/api/check-exports new file mode 100755 index 0000000..5c82064 --- /dev/null +++ b/lib/jansson/test/suites/api/check-exports @@ -0,0 +1,23 @@ +#!/bin/sh +# +# This test checks that libjansson.so exports the correct symbols. +# + +SOFILE="../src/.libs/libjansson.so" + +# The list of symbols, which the shared object should export, is read +# from the def file, which is used in Windows builds +grep 'json_\|jansson_' $top_srcdir/src/jansson.def \ + | sed -e 's/ //g' \ + | sort \ + >$test_log/exports + +nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \ + || exit 77 # Skip if "nm -D" doesn't seem to work + +grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | grep -v '^_' | sed 's/@@libjansson.*//' | sort >$test_log/output + +if ! cmp -s $test_log/exports $test_log/output; then + diff -u $test_log/exports $test_log/output >&2 + exit 1 +fi diff --git a/lib/jansson/test/suites/api/run b/lib/jansson/test/suites/api/run new file mode 100755 index 0000000..0c017bc --- /dev/null +++ b/lib/jansson/test/suites/api/run @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +is_test() { + case "$test_name" in + *.c|check-exports) + return 0 + ;; + *) + return 1 + ;; + esac +} + +run_test() { + if [ "$test_name" = "check-exports" ]; then + test_log=$test_log $test_path >$test_log/stdout 2>$test_log/stderr + else + $test_runner $suite_builddir/${test_name%.c} \ + >$test_log/stdout \ + 2>$test_log/stderr \ + || return 1 + valgrind_check $test_log/stderr || return 1 + fi +} + +show_error() { + valgrind_show_error && return + cat $test_log/stderr +} + +. $top_srcdir/test/scripts/run-tests.sh diff --git a/lib/jansson/test/suites/api/test_array.c b/lib/jansson/test/suites/api/test_array.c new file mode 100644 index 0000000..e5d9d1a --- /dev/null +++ b/lib/jansson/test/suites/api/test_array.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include + +static void test_misc(void) { + json_t *array, *five, *seven, *value; + size_t i; + + array = json_array(); + five = json_integer(5); + seven = json_integer(7); + + if (!array) + fail("unable to create array"); + if (!five || !seven) + fail("unable to create integer"); + + if (json_array_size(array) != 0) + fail("empty array has nonzero size"); + + if (!json_array_append(array, NULL)) + fail("able to append NULL"); + + if (json_array_append(array, five)) + fail("unable to append"); + + if (json_array_size(array) != 1) + fail("wrong array size"); + + value = json_array_get(array, 0); + if (!value) + fail("unable to get item"); + if (value != five) + fail("got wrong value"); + + if (json_array_append(array, seven)) + fail("unable to append value"); + + if (json_array_size(array) != 2) + fail("wrong array size"); + + value = json_array_get(array, 1); + if (!value) + fail("unable to get item"); + if (value != seven) + fail("got wrong value"); + + if (json_array_set(array, 0, seven)) + fail("unable to set value"); + + if (!json_array_set(array, 0, NULL)) + fail("able to set NULL"); + + if (json_array_size(array) != 2) + fail("wrong array size"); + + value = json_array_get(array, 0); + if (!value) + fail("unable to get item"); + if (value != seven) + fail("got wrong value"); + + if (json_array_get(array, 2) != NULL) + fail("able to get value out of bounds"); + + if (!json_array_set(array, 2, seven)) + fail("able to set value out of bounds"); + + for (i = 2; i < 30; i++) { + if (json_array_append(array, seven)) + fail("unable to append value"); + + if (json_array_size(array) != i + 1) + fail("wrong array size"); + } + + for (i = 0; i < 30; i++) { + value = json_array_get(array, i); + if (!value) + fail("unable to get item"); + if (value != seven) + fail("got wrong value"); + } + + if (json_array_set_new(array, 15, json_integer(123))) + fail("unable to set new value"); + + value = json_array_get(array, 15); + if (!json_is_integer(value) || json_integer_value(value) != 123) + fail("json_array_set_new works incorrectly"); + + if (!json_array_set_new(array, 15, NULL)) + fail("able to set_new NULL value"); + + if (json_array_append_new(array, json_integer(321))) + fail("unable to append new value"); + + value = json_array_get(array, json_array_size(array) - 1); + if (!json_is_integer(value) || json_integer_value(value) != 321) + fail("json_array_append_new works incorrectly"); + + if (!json_array_append_new(array, NULL)) + fail("able to append_new NULL value"); + + json_decref(five); + json_decref(seven); + json_decref(array); +} + +static void test_insert(void) { + json_t *array, *five, *seven, *eleven, *value; + int i; + + array = json_array(); + five = json_integer(5); + seven = json_integer(7); + eleven = json_integer(11); + + if (!array) + fail("unable to create array"); + if (!five || !seven || !eleven) + fail("unable to create integer"); + + if (!json_array_insert(array, 1, five)) + fail("able to insert value out of bounds"); + + if (json_array_insert(array, 0, five)) + fail("unable to insert value in an empty array"); + + if (json_array_get(array, 0) != five) + fail("json_array_insert works incorrectly"); + + if (json_array_size(array) != 1) + fail("array size is invalid after insertion"); + + if (json_array_insert(array, 1, seven)) + fail("unable to insert value at the end of an array"); + + if (json_array_get(array, 0) != five) + fail("json_array_insert works incorrectly"); + + if (json_array_get(array, 1) != seven) + fail("json_array_insert works incorrectly"); + + if (json_array_size(array) != 2) + fail("array size is invalid after insertion"); + + if (json_array_insert(array, 1, eleven)) + fail("unable to insert value in the middle of an array"); + + if (json_array_get(array, 0) != five) + fail("json_array_insert works incorrectly"); + + if (json_array_get(array, 1) != eleven) + fail("json_array_insert works incorrectly"); + + if (json_array_get(array, 2) != seven) + fail("json_array_insert works incorrectly"); + + if (json_array_size(array) != 3) + fail("array size is invalid after insertion"); + + if (json_array_insert_new(array, 2, json_integer(123))) + fail("unable to insert value in the middle of an array"); + + value = json_array_get(array, 2); + if (!json_is_integer(value) || json_integer_value(value) != 123) + fail("json_array_insert_new works incorrectly"); + + if (json_array_size(array) != 4) + fail("array size is invalid after insertion"); + + for (i = 0; i < 20; i++) { + if (json_array_insert(array, 0, seven)) + fail("unable to insert value at the beginning of an array"); + } + + for (i = 0; i < 20; i++) { + if (json_array_get(array, i) != seven) + fail("json_aray_insert works incorrectly"); + } + + if (json_array_size(array) != 24) + fail("array size is invalid after loop insertion"); + + json_decref(five); + json_decref(seven); + json_decref(eleven); + json_decref(array); +} + +static void test_remove(void) { + json_t *array, *five, *seven; + int i; + + array = json_array(); + five = json_integer(5); + seven = json_integer(7); + + if (!array) + fail("unable to create array"); + if (!five) + fail("unable to create integer"); + if (!seven) + fail("unable to create integer"); + + if (!json_array_remove(array, 0)) + fail("able to remove an unexisting index"); + + if (json_array_append(array, five)) + fail("unable to append"); + + if (!json_array_remove(array, 1)) + fail("able to remove an unexisting index"); + + if (json_array_remove(array, 0)) + fail("unable to remove"); + + if (json_array_size(array) != 0) + fail("array size is invalid after removing"); + + if (json_array_append(array, five) || json_array_append(array, seven) || + json_array_append(array, five) || json_array_append(array, seven)) + fail("unable to append"); + + if (json_array_remove(array, 2)) + fail("unable to remove"); + + if (json_array_size(array) != 3) + fail("array size is invalid after removing"); + + if (json_array_get(array, 0) != five || json_array_get(array, 1) != seven || + json_array_get(array, 2) != seven) + fail("remove works incorrectly"); + + json_decref(array); + + array = json_array(); + for (i = 0; i < 4; i++) { + json_array_append(array, five); + json_array_append(array, seven); + } + if (json_array_size(array) != 8) + fail("unable to append 8 items to array"); + + /* Remove an element from a "full" array. */ + json_array_remove(array, 5); + + json_decref(five); + json_decref(seven); + json_decref(array); +} + +static void test_clear(void) { + json_t *array, *five, *seven; + int i; + + array = json_array(); + five = json_integer(5); + seven = json_integer(7); + + if (!array) + fail("unable to create array"); + if (!five || !seven) + fail("unable to create integer"); + + for (i = 0; i < 10; i++) { + if (json_array_append(array, five)) + fail("unable to append"); + } + for (i = 0; i < 10; i++) { + if (json_array_append(array, seven)) + fail("unable to append"); + } + + if (json_array_size(array) != 20) + fail("array size is invalid after appending"); + + if (json_array_clear(array)) + fail("unable to clear"); + + if (json_array_size(array) != 0) + fail("array size is invalid after clearing"); + + json_decref(five); + json_decref(seven); + json_decref(array); +} + +static void test_extend(void) { + json_t *array1, *array2, *five, *seven; + int i; + + array1 = json_array(); + array2 = json_array(); + five = json_integer(5); + seven = json_integer(7); + + if (!array1 || !array2) + fail("unable to create array"); + if (!five || !seven) + fail("unable to create integer"); + + for (i = 0; i < 10; i++) { + if (json_array_append(array1, five)) + fail("unable to append"); + } + for (i = 0; i < 10; i++) { + if (json_array_append(array2, seven)) + fail("unable to append"); + } + + if (json_array_size(array1) != 10 || json_array_size(array2) != 10) + fail("array size is invalid after appending"); + + if (json_array_extend(array1, array2)) + fail("unable to extend"); + + for (i = 0; i < 10; i++) { + if (json_array_get(array1, i) != five) + fail("invalid array contents after extending"); + } + for (i = 10; i < 20; i++) { + if (json_array_get(array1, i) != seven) + fail("invalid array contents after extending"); + } + + json_decref(five); + json_decref(seven); + json_decref(array1); + json_decref(array2); +} + +static void test_circular() { + json_t *array1, *array2; + + /* the simple cases are checked */ + + array1 = json_array(); + if (!array1) + fail("unable to create array"); + + if (json_array_append(array1, array1) == 0) + fail("able to append self"); + + if (json_array_insert(array1, 0, array1) == 0) + fail("able to insert self"); + + if (json_array_append_new(array1, json_true())) + fail("failed to append true"); + + if (json_array_set(array1, 0, array1) == 0) + fail("able to set self"); + + json_decref(array1); + + /* create circular references */ + + array1 = json_array(); + array2 = json_array(); + if (!array1 || !array2) + fail("unable to create array"); + + if (json_array_append(array1, array2) || json_array_append(array2, array1)) + fail("unable to append"); + + /* circularity is detected when dumping */ + if (json_dumps(array1, 0) != NULL) + fail("able to dump circulars"); + + /* decref twice to deal with the circular references */ + json_decref(array1); + json_decref(array2); + json_decref(array1); +} + +static void test_array_foreach() { + size_t index; + json_t *array1, *array2, *value; + + array1 = json_pack("[sisisi]", "foo", 1, "bar", 2, "baz", 3); + array2 = json_array(); + + json_array_foreach(array1, index, value) { json_array_append(array2, value); } + + if (!json_equal(array1, array2)) + fail("json_array_foreach failed to iterate all elements"); + + json_decref(array1); + json_decref(array2); +} + +static void test_bad_args(void) { + json_t *arr = json_array(); + json_t *num = json_integer(1); + + if (!arr || !num) + fail("failed to create required objects"); + + if (json_array_size(NULL) != 0) + fail("NULL array has nonzero size"); + if (json_array_size(num) != 0) + fail("non-array has nonzero array size"); + + if (json_array_get(NULL, 0)) + fail("json_array_get did not return NULL for non-array"); + if (json_array_get(num, 0)) + fail("json_array_get did not return NULL for non-array"); + + if (!json_array_set_new(NULL, 0, json_incref(num))) + fail("json_array_set_new did not return error for non-array"); + if (!json_array_set_new(num, 0, json_incref(num))) + fail("json_array_set_new did not return error for non-array"); + if (!json_array_set_new(arr, 0, NULL)) + fail("json_array_set_new did not return error for NULL value"); + if (!json_array_set_new(arr, 0, json_incref(arr))) + fail("json_array_set_new did not return error for value == array"); + + if (!json_array_remove(NULL, 0)) + fail("json_array_remove did not return error for non-array"); + if (!json_array_remove(num, 0)) + fail("json_array_remove did not return error for non-array"); + + if (!json_array_clear(NULL)) + fail("json_array_clear did not return error for non-array"); + if (!json_array_clear(num)) + fail("json_array_clear did not return error for non-array"); + + if (!json_array_append_new(NULL, json_incref(num))) + fail("json_array_append_new did not return error for non-array"); + if (!json_array_append_new(num, json_incref(num))) + fail("json_array_append_new did not return error for non-array"); + if (!json_array_append_new(arr, NULL)) + fail("json_array_append_new did not return error for NULL value"); + if (!json_array_append_new(arr, json_incref(arr))) + fail("json_array_append_new did not return error for value == array"); + + if (!json_array_insert_new(NULL, 0, json_incref(num))) + fail("json_array_insert_new did not return error for non-array"); + if (!json_array_insert_new(num, 0, json_incref(num))) + fail("json_array_insert_new did not return error for non-array"); + if (!json_array_insert_new(arr, 0, NULL)) + fail("json_array_insert_new did not return error for NULL value"); + if (!json_array_insert_new(arr, 0, json_incref(arr))) + fail("json_array_insert_new did not return error for value == array"); + + if (!json_array_extend(NULL, arr)) + fail("json_array_extend did not return error for first argument " + "non-array"); + if (!json_array_extend(num, arr)) + fail("json_array_extend did not return error for first argument " + "non-array"); + if (!json_array_extend(arr, NULL)) + fail("json_array_extend did not return error for second argument " + "non-array"); + if (!json_array_extend(arr, num)) + fail("json_array_extend did not return error for second argument " + "non-array"); + + if (num->refcount != 1) + fail("unexpected reference count on num"); + if (arr->refcount != 1) + fail("unexpected reference count on arr"); + + json_decref(num); + json_decref(arr); +} + +static void run_tests() { + test_misc(); + test_insert(); + test_remove(); + test_clear(); + test_extend(); + test_circular(); + test_array_foreach(); + test_bad_args(); +} diff --git a/lib/jansson/test/suites/api/test_chaos.c b/lib/jansson/test/suites/api/test_chaos.c new file mode 100644 index 0000000..8687243 --- /dev/null +++ b/lib/jansson/test/suites/api/test_chaos.c @@ -0,0 +1,168 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "util.h" +#include +#include +#include + +static int chaos_pos = 0; +static int chaos_fail = 0; +#define CHAOS_MAX_FAILURE 100 + +void *chaos_malloc(size_t size) { + if (chaos_pos == chaos_fail) + return NULL; + + chaos_pos++; + + return malloc(size); +} + +void chaos_free(void *obj) { free(obj); } + +/* Test all potential allocation failures. */ +#define chaos_loop(condition, code, cleanup) \ + { \ + chaos_pos = chaos_fail = 0; \ + while (condition) { \ + if (chaos_fail > CHAOS_MAX_FAILURE) \ + fail("too many chaos failures"); \ + code chaos_pos = 0; \ + chaos_fail++; \ + } \ + cleanup \ + } + +#define chaos_loop_new_value(json, initcall) \ + chaos_loop(!json, json = initcall;, json_decref(json); json = NULL;) + +int test_unpack() { + int ret = -1; + int v1; + int v2; + json_error_t error; + json_t *root = json_pack("{s:i, s:i, s:i, s:i}", "n1", 1, "n2", 2, "n3", 3, "n4", 4); + + if (!root) + return -1; + + if (!json_unpack_ex(root, &error, JSON_STRICT, "{s:i, s:i}", "n1", &v1, "n2", &v2)) + fail("Unexpected success"); + + if (json_error_code(&error) != json_error_end_of_input_expected) { + if (json_error_code(&error) != json_error_out_of_memory) + fail("Unexpected error code"); + + goto out; + } + + if (strcmp(error.text, "2 object item(s) left unpacked: n3, n4")) + goto out; + + ret = 0; + +out: + json_decref(root); + return ret; +} + +int dump_chaos_callback(const char *buffer, size_t size, void *data) { + json_t *obj = json_object(); + + (void)buffer; + (void)size; + (void)data; + + if (!obj) + return -1; + + json_decref(obj); + + return 0; +} + +static void test_chaos() { + json_malloc_t orig_malloc; + json_free_t orig_free; + json_t *json = NULL; + json_t *obj = json_object(); + json_t *arr1 = json_array(); + json_t *arr2 = json_array(); + json_t *txt = json_string("test"); + json_t *intnum = json_integer(1); + json_t *dblnum = json_real(0.5); + char *dumptxt = NULL; + json_t *dumpobj = json_pack("{s:[iiis], s:s}", "key1", 1, 2, 3, "txt", "key2", "v2"); + int keyno; + + if (!obj || !arr1 || !arr2 || !txt || !intnum || !dblnum || !dumpobj) + fail("failed to allocate basic objects"); + + json_get_alloc_funcs(&orig_malloc, &orig_free); + json_set_alloc_funcs(chaos_malloc, chaos_free); + + chaos_loop_new_value(json, json_pack("{s:s}", "key", "value")); + chaos_loop_new_value(json, json_pack("{s:[]}", "key")); + chaos_loop_new_value(json, json_pack("[biIf]", 1, 1, (json_int_t)1, 1.0)); + chaos_loop_new_value(json, json_pack("[s*,s*]", "v1", "v2")); + chaos_loop_new_value(json, json_pack("o", json_incref(txt))); + chaos_loop_new_value(json, json_pack("O", txt)); + chaos_loop_new_value(json, json_pack("s++", "a", "long string to force realloc", + "another long string to force yet another " + "reallocation of the string because " + "that's what we are testing.")); + + chaos_loop(test_unpack(), , ); + + chaos_loop(json_dump_callback(dumpobj, dump_chaos_callback, NULL, JSON_INDENT(1)), + , ); + chaos_loop(json_dump_callback(dumpobj, dump_chaos_callback, NULL, + JSON_INDENT(1) | JSON_SORT_KEYS), + , ); + chaos_loop(!dumptxt, dumptxt = json_dumps(dumpobj, JSON_COMPACT);, free(dumptxt); + dumptxt = NULL;); + + chaos_loop_new_value(json, json_copy(obj)); + chaos_loop_new_value(json, json_deep_copy(obj)); + + chaos_loop_new_value(json, json_copy(arr1)); + chaos_loop_new_value(json, json_deep_copy(arr1)); + + chaos_loop_new_value(json, json_copy(txt)); + chaos_loop_new_value(json, json_copy(intnum)); + chaos_loop_new_value(json, json_copy(dblnum)); + +#define JSON_LOAD_TXT "{\"n\":[1,2,3,4,5,6,7,8,9,10]}" + chaos_loop_new_value(json, json_loads(JSON_LOAD_TXT, 0, NULL)); + chaos_loop_new_value(json, json_loadb(JSON_LOAD_TXT, strlen(JSON_LOAD_TXT), 0, NULL)); + + chaos_loop_new_value(json, json_sprintf("%s", "string")); + + for (keyno = 0; keyno < 100; ++keyno) { +#if !defined(_MSC_VER) || _MSC_VER >= 1900 + /* Skip this test on old Windows compilers. */ + char testkey[10]; + + snprintf(testkey, sizeof(testkey), "test%d", keyno); + chaos_loop(json_object_set_new_nocheck(obj, testkey, json_object()), , ); +#endif + chaos_loop(json_array_append_new(arr1, json_null()), , ); + chaos_loop(json_array_insert_new(arr2, 0, json_null()), , ); + } + + chaos_loop(json_array_extend(arr1, arr2), , ); + chaos_loop(json_string_set_nocheck(txt, "test"), , ); + + json_set_alloc_funcs(orig_malloc, orig_free); + json_decref(obj); + json_decref(arr1); + json_decref(arr2); + json_decref(txt); + json_decref(intnum); + json_decref(dblnum); + json_decref(dumpobj); +} + +static void run_tests() { test_chaos(); } diff --git a/lib/jansson/test/suites/api/test_copy.c b/lib/jansson/test/suites/api/test_copy.c new file mode 100644 index 0000000..656d98d --- /dev/null +++ b/lib/jansson/test/suites/api/test_copy.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void test_copy_simple(void) { + json_t *value, *copy; + + if (json_copy(NULL)) + fail("copying NULL doesn't return NULL"); + + /* true */ + value = json_true(); + copy = json_copy(value); + if (value != copy) + fail("copying true failed"); + json_decref(value); + json_decref(copy); + + /* false */ + value = json_false(); + copy = json_copy(value); + if (value != copy) + fail("copying false failed"); + json_decref(value); + json_decref(copy); + + /* null */ + value = json_null(); + copy = json_copy(value); + if (value != copy) + fail("copying null failed"); + json_decref(value); + json_decref(copy); + + /* string */ + value = json_string("foo"); + if (!value) + fail("unable to create a string"); + copy = json_copy(value); + if (!copy) + fail("unable to copy a string"); + if (copy == value) + fail("copying a string doesn't copy"); + if (!json_equal(copy, value)) + fail("copying a string produces an inequal copy"); + if (value->refcount != 1 || copy->refcount != 1) + fail("invalid refcounts"); + json_decref(value); + json_decref(copy); + + /* integer */ + value = json_integer(543); + if (!value) + fail("unable to create an integer"); + copy = json_copy(value); + if (!copy) + fail("unable to copy an integer"); + if (copy == value) + fail("copying an integer doesn't copy"); + if (!json_equal(copy, value)) + fail("copying an integer produces an inequal copy"); + if (value->refcount != 1 || copy->refcount != 1) + fail("invalid refcounts"); + json_decref(value); + json_decref(copy); + + /* real */ + value = json_real(123e9); + if (!value) + fail("unable to create a real"); + copy = json_copy(value); + if (!copy) + fail("unable to copy a real"); + if (copy == value) + fail("copying a real doesn't copy"); + if (!json_equal(copy, value)) + fail("copying a real produces an inequal copy"); + if (value->refcount != 1 || copy->refcount != 1) + fail("invalid refcounts"); + json_decref(value); + json_decref(copy); +} + +static void test_deep_copy_simple(void) { + json_t *value, *copy; + + if (json_deep_copy(NULL)) + fail("deep copying NULL doesn't return NULL"); + + /* true */ + value = json_true(); + copy = json_deep_copy(value); + if (value != copy) + fail("deep copying true failed"); + json_decref(value); + json_decref(copy); + + /* false */ + value = json_false(); + copy = json_deep_copy(value); + if (value != copy) + fail("deep copying false failed"); + json_decref(value); + json_decref(copy); + + /* null */ + value = json_null(); + copy = json_deep_copy(value); + if (value != copy) + fail("deep copying null failed"); + json_decref(value); + json_decref(copy); + + /* string */ + value = json_string("foo"); + if (!value) + fail("unable to create a string"); + copy = json_deep_copy(value); + if (!copy) + fail("unable to deep copy a string"); + if (copy == value) + fail("deep copying a string doesn't copy"); + if (!json_equal(copy, value)) + fail("deep copying a string produces an inequal copy"); + if (value->refcount != 1 || copy->refcount != 1) + fail("invalid refcounts"); + json_decref(value); + json_decref(copy); + + /* integer */ + value = json_integer(543); + if (!value) + fail("unable to create an integer"); + copy = json_deep_copy(value); + if (!copy) + fail("unable to deep copy an integer"); + if (copy == value) + fail("deep copying an integer doesn't copy"); + if (!json_equal(copy, value)) + fail("deep copying an integer produces an inequal copy"); + if (value->refcount != 1 || copy->refcount != 1) + fail("invalid refcounts"); + json_decref(value); + json_decref(copy); + + /* real */ + value = json_real(123e9); + if (!value) + fail("unable to create a real"); + copy = json_deep_copy(value); + if (!copy) + fail("unable to deep copy a real"); + if (copy == value) + fail("deep copying a real doesn't copy"); + if (!json_equal(copy, value)) + fail("deep copying a real produces an inequal copy"); + if (value->refcount != 1 || copy->refcount != 1) + fail("invalid refcounts"); + json_decref(value); + json_decref(copy); +} + +static void test_copy_array(void) { + const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]"; + + json_t *array, *copy; + size_t i; + + array = json_loads(json_array_text, 0, NULL); + if (!array) + fail("unable to parse an array"); + + copy = json_copy(array); + if (!copy) + fail("unable to copy an array"); + if (copy == array) + fail("copying an array doesn't copy"); + if (!json_equal(copy, array)) + fail("copying an array produces an inequal copy"); + + for (i = 0; i < json_array_size(copy); i++) { + if (json_array_get(array, i) != json_array_get(copy, i)) + fail("copying an array modifies its elements"); + } + + json_decref(array); + json_decref(copy); +} + +static void test_deep_copy_array(void) { + const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]"; + + json_t *array, *copy; + size_t i; + + array = json_loads(json_array_text, 0, NULL); + if (!array) + fail("unable to parse an array"); + + copy = json_deep_copy(array); + if (!copy) + fail("unable to deep copy an array"); + if (copy == array) + fail("deep copying an array doesn't copy"); + if (!json_equal(copy, array)) + fail("deep copying an array produces an inequal copy"); + + for (i = 0; i < json_array_size(copy); i++) { + if (json_array_get(array, i) == json_array_get(copy, i)) + fail("deep copying an array doesn't copy its elements"); + } + + json_decref(array); + json_decref(copy); +} + +static void test_copy_object(void) { + const char *json_object_text = + "{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}"; + + const char *keys[] = {"foo", "a", "b", "c"}; + int i; + + json_t *object, *copy; + void *iter; + + object = json_loads(json_object_text, 0, NULL); + if (!object) + fail("unable to parse an object"); + + copy = json_copy(object); + if (!copy) + fail("unable to copy an object"); + if (copy == object) + fail("copying an object doesn't copy"); + if (!json_equal(copy, object)) + fail("copying an object produces an inequal copy"); + + i = 0; + iter = json_object_iter(object); + while (iter) { + const char *key; + json_t *value1, *value2; + + key = json_object_iter_key(iter); + value1 = json_object_iter_value(iter); + value2 = json_object_get(copy, key); + + if (value1 != value2) + fail("copying an object modifies its items"); + + if (strcmp(key, keys[i]) != 0) + fail("copying an object doesn't preserve key order"); + + iter = json_object_iter_next(object, iter); + i++; + } + + json_decref(object); + json_decref(copy); +} + +static void test_deep_copy_object(void) { + const char *json_object_text = + "{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}"; + + const char *keys[] = {"foo", "a", "b", "c"}; + int i; + + json_t *object, *copy; + void *iter; + + object = json_loads(json_object_text, 0, NULL); + if (!object) + fail("unable to parse an object"); + + copy = json_deep_copy(object); + if (!copy) + fail("unable to deep copy an object"); + if (copy == object) + fail("deep copying an object doesn't copy"); + if (!json_equal(copy, object)) + fail("deep copying an object produces an inequal copy"); + + i = 0; + iter = json_object_iter(object); + while (iter) { + const char *key; + json_t *value1, *value2; + + key = json_object_iter_key(iter); + value1 = json_object_iter_value(iter); + value2 = json_object_get(copy, key); + + if (value1 == value2) + fail("deep copying an object doesn't copy its items"); + + if (strcmp(key, keys[i]) != 0) + fail("deep copying an object doesn't preserve key order"); + + iter = json_object_iter_next(object, iter); + i++; + } + + json_decref(object); + json_decref(copy); +} + +static void test_deep_copy_circular_references(void) { + /* Construct a JSON object/array with a circular reference: + + object: {"a": {"b": {"c": }}} + array: [[[]]] + + Deep copy it, remove the circular reference and deep copy again. + */ + + json_t *json; + json_t *copy; + + json = json_object(); + json_object_set_new(json, "a", json_object()); + json_object_set_new(json_object_get(json, "a"), "b", json_object()); + json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c", + json_object_get(json, "a")); + + copy = json_deep_copy(json); + if (copy) + fail("json_deep_copy copied a circular reference!"); + + json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c"); + + copy = json_deep_copy(json); + if (!copy) + fail("json_deep_copy failed!"); + + json_decref(copy); + json_decref(json); + + json = json_array(); + json_array_append_new(json, json_array()); + json_array_append_new(json_array_get(json, 0), json_array()); + json_array_append(json_array_get(json_array_get(json, 0), 0), + json_array_get(json, 0)); + + copy = json_deep_copy(json); + if (copy) + fail("json_deep_copy copied a circular reference!"); + + json_array_remove(json_array_get(json_array_get(json, 0), 0), 0); + + copy = json_deep_copy(json); + if (!copy) + fail("json_deep_copy failed!"); + + json_decref(copy); + json_decref(json); +} + +static void run_tests() { + test_copy_simple(); + test_deep_copy_simple(); + test_copy_array(); + test_deep_copy_array(); + test_copy_object(); + test_deep_copy_object(); + test_deep_copy_circular_references(); +} diff --git a/lib/jansson/test/suites/api/test_dump.c b/lib/jansson/test/suites/api/test_dump.c new file mode 100644 index 0000000..e8cb519 --- /dev/null +++ b/lib/jansson/test/suites/api/test_dump.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "jansson_private_config.h" + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "util.h" +#ifdef __MINGW32__ +#include +#define pipe(fds) _pipe(fds, 1024, _O_BINARY) +#endif + +static int encode_null_callback(const char *buffer, size_t size, void *data) { + (void)buffer; + (void)size; + (void)data; + return 0; +} + +static void encode_null() { + if (json_dumps(NULL, JSON_ENCODE_ANY) != NULL) + fail("json_dumps didn't fail for NULL"); + + if (json_dumpb(NULL, NULL, 0, JSON_ENCODE_ANY) != 0) + fail("json_dumpb didn't fail for NULL"); + + if (json_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1) + fail("json_dumpf didn't fail for NULL"); + +#ifdef HAVE_UNISTD_H + if (json_dumpfd(NULL, STDERR_FILENO, JSON_ENCODE_ANY) != -1) + fail("json_dumpfd didn't fail for NULL"); +#endif + + /* Don't test json_dump_file to avoid creating a file */ + + if (json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1) + fail("json_dump_callback didn't fail for NULL"); +} + +static void encode_twice() { + /* Encode an empty object/array, add an item, encode again */ + + json_t *json; + char *result; + + json = json_object(); + result = json_dumps(json, 0); + if (!result || strcmp(result, "{}")) + fail("json_dumps failed"); + free(result); + + json_object_set_new(json, "foo", json_integer(5)); + result = json_dumps(json, 0); + if (!result || strcmp(result, "{\"foo\": 5}")) + fail("json_dumps failed"); + free(result); + + json_decref(json); + + json = json_array(); + result = json_dumps(json, 0); + if (!result || strcmp(result, "[]")) + fail("json_dumps failed"); + free(result); + + json_array_append_new(json, json_integer(5)); + result = json_dumps(json, 0); + if (!result || strcmp(result, "[5]")) + fail("json_dumps failed"); + free(result); + + json_decref(json); +} + +static void circular_references() { + /* Construct a JSON object/array with a circular reference: + + object: {"a": {"b": {"c": }}} + array: [[[]]] + + Encode it, remove the circular reference and encode again. + */ + + json_t *json; + char *result; + + json = json_object(); + json_object_set_new(json, "a", json_object()); + json_object_set_new(json_object_get(json, "a"), "b", json_object()); + json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c", + json_object_get(json, "a")); + + if (json_dumps(json, 0)) + fail("json_dumps encoded a circular reference!"); + + json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c"); + + result = json_dumps(json, 0); + if (!result || strcmp(result, "{\"a\": {\"b\": {}}}")) + fail("json_dumps failed!"); + free(result); + + json_decref(json); + + json = json_array(); + json_array_append_new(json, json_array()); + json_array_append_new(json_array_get(json, 0), json_array()); + json_array_append(json_array_get(json_array_get(json, 0), 0), + json_array_get(json, 0)); + + if (json_dumps(json, 0)) + fail("json_dumps encoded a circular reference!"); + + json_array_remove(json_array_get(json_array_get(json, 0), 0), 0); + + result = json_dumps(json, 0); + if (!result || strcmp(result, "[[[]]]")) + fail("json_dumps failed!"); + free(result); + + json_decref(json); +} + +static void encode_other_than_array_or_object() { + /* Encoding anything other than array or object should only + * succeed if the JSON_ENCODE_ANY flag is used */ + + json_t *json; + char *result; + + json = json_string("foo"); + if (json_dumps(json, 0) != NULL) + fail("json_dumps encoded a string!"); + if (json_dumpf(json, NULL, 0) == 0) + fail("json_dumpf encoded a string!"); + if (json_dumpfd(json, -1, 0) == 0) + fail("json_dumpfd encoded a string!"); + + result = json_dumps(json, JSON_ENCODE_ANY); + if (!result || strcmp(result, "\"foo\"") != 0) + fail("json_dumps failed to encode a string with JSON_ENCODE_ANY"); + + free(result); + json_decref(json); + + json = json_integer(42); + if (json_dumps(json, 0) != NULL) + fail("json_dumps encoded an integer!"); + if (json_dumpf(json, NULL, 0) == 0) + fail("json_dumpf encoded an integer!"); + if (json_dumpfd(json, -1, 0) == 0) + fail("json_dumpfd encoded an integer!"); + + result = json_dumps(json, JSON_ENCODE_ANY); + if (!result || strcmp(result, "42") != 0) + fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY"); + + free(result); + json_decref(json); +} + +static void escape_slashes() { + /* Test dump escaping slashes */ + + json_t *json; + char *result; + + json = json_object(); + json_object_set_new(json, "url", json_string("https://github.com/akheron/jansson")); + + result = json_dumps(json, 0); + if (!result || strcmp(result, "{\"url\": \"https://github.com/akheron/jansson\"}")) + fail("json_dumps failed to not escape slashes"); + + free(result); + + result = json_dumps(json, JSON_ESCAPE_SLASH); + if (!result || + strcmp(result, "{\"url\": \"https:\\/\\/github.com\\/akheron\\/jansson\"}")) + fail("json_dumps failed to escape slashes"); + + free(result); + json_decref(json); +} + +static void encode_nul_byte() { + json_t *json; + char *result; + + json = json_stringn("nul byte \0 in string", 20); + result = json_dumps(json, JSON_ENCODE_ANY); + if (!result || memcmp(result, "\"nul byte \\u0000 in string\"", 27)) + fail("json_dumps failed to dump an embedded NUL byte"); + + free(result); + json_decref(json); +} + +static void dump_file() { + json_t *json; + int result; + + result = json_dump_file(NULL, "", 0); + if (result != -1) + fail("json_dump_file succeeded with invalid args"); + + json = json_object(); + result = json_dump_file(json, "json_dump_file.json", 0); + if (result != 0) + fail("json_dump_file failed"); + + json_decref(json); + remove("json_dump_file.json"); +} + +static void dumpb() { + char buf[2]; + json_t *obj; + size_t size; + + obj = json_object(); + + size = json_dumpb(obj, buf, sizeof(buf), 0); + if (size != 2 || strncmp(buf, "{}", 2)) + fail("json_dumpb failed"); + + json_decref(obj); + obj = json_pack("{s:s}", "foo", "bar"); + + size = json_dumpb(obj, buf, sizeof(buf), JSON_COMPACT); + if (size != 13) + fail("json_dumpb size check failed"); + + json_decref(obj); +} + +static void dumpfd() { +#ifdef HAVE_UNISTD_H + int fds[2] = {-1, -1}; + json_t *a, *b; + + if (pipe(fds)) + fail("pipe() failed"); + + a = json_pack("{s:s}", "foo", "bar"); + + if (json_dumpfd(a, fds[1], 0)) + fail("json_dumpfd() failed"); + close(fds[1]); + + b = json_loadfd(fds[0], 0, NULL); + if (!b) + fail("json_loadfd() failed"); + close(fds[0]); + + if (!json_equal(a, b)) + fail("json_equal() failed for fd test"); + + json_decref(a); + json_decref(b); +#endif +} + +static void embed() { + static const char *plains[] = {"{\"bar\":[],\"foo\":{}}", "[[],{}]", "{}", "[]", + NULL}; + + size_t i; + + for (i = 0; plains[i]; i++) { + const char *plain = plains[i]; + json_t *parse = NULL; + char *embed = NULL; + size_t psize = 0; + size_t esize = 0; + + psize = strlen(plain) - 2; + embed = calloc(1, psize); + parse = json_loads(plain, 0, NULL); + esize = + json_dumpb(parse, embed, psize, JSON_COMPACT | JSON_SORT_KEYS | JSON_EMBED); + json_decref(parse); + if (esize != psize) + fail("json_dumpb(JSON_EMBED) returned an invalid size"); + if (strncmp(plain + 1, embed, esize) != 0) + fail("json_dumps(JSON_EMBED) returned an invalid value"); + free(embed); + } +} + +static void run_tests() { + encode_null(); + encode_twice(); + circular_references(); + encode_other_than_array_or_object(); + escape_slashes(); + encode_nul_byte(); + dump_file(); + dumpb(); + dumpfd(); + embed(); +} diff --git a/lib/jansson/test/suites/api/test_dump_callback.c b/lib/jansson/test/suites/api/test_dump_callback.c new file mode 100644 index 0000000..80ea008 --- /dev/null +++ b/lib/jansson/test/suites/api/test_dump_callback.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include +#include + +struct my_sink { + char *buf; + size_t off; + size_t cap; +}; + +static int my_writer(const char *buffer, size_t len, void *data) { + struct my_sink *s = data; + if (len > s->cap - s->off) { + return -1; + } + memcpy(s->buf + s->off, buffer, len); + s->off += len; + return 0; +} + +static void run_tests() { + struct my_sink s; + json_t *json; + const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]"; + char *dumped_to_string; + + json = json_loads(str, 0, NULL); + if (!json) { + fail("json_loads failed"); + } + + dumped_to_string = json_dumps(json, 0); + if (!dumped_to_string) { + json_decref(json); + fail("json_dumps failed"); + } + + s.off = 0; + s.cap = strlen(dumped_to_string); + s.buf = malloc(s.cap); + if (!s.buf) { + json_decref(json); + free(dumped_to_string); + fail("malloc failed"); + } + + if (json_dump_callback(json, my_writer, &s, 0) == -1) { + json_decref(json); + free(dumped_to_string); + free(s.buf); + fail("json_dump_callback failed on an exact-length sink buffer"); + } + + if (strncmp(dumped_to_string, s.buf, s.off) != 0) { + json_decref(json); + free(dumped_to_string); + free(s.buf); + fail("json_dump_callback and json_dumps did not produce identical " + "output"); + } + + s.off = 1; + if (json_dump_callback(json, my_writer, &s, 0) != -1) { + json_decref(json); + free(dumped_to_string); + free(s.buf); + fail("json_dump_callback succeeded on a short buffer when it should " + "have failed"); + } + + json_decref(json); + free(dumped_to_string); + free(s.buf); +} diff --git a/lib/jansson/test/suites/api/test_equal.c b/lib/jansson/test/suites/api/test_equal.c new file mode 100644 index 0000000..d068b7b --- /dev/null +++ b/lib/jansson/test/suites/api/test_equal.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include + +static void test_equal_simple() { + json_t *value1, *value2; + + if (json_equal(NULL, NULL)) + fail("json_equal fails for two NULLs"); + + value1 = json_true(); + if (json_equal(value1, NULL) || json_equal(NULL, value1)) + fail("json_equal fails for NULL"); + + /* this covers true, false and null as they are singletons */ + if (!json_equal(value1, value1)) + fail("identical objects are not equal"); + json_decref(value1); + + /* integer */ + value1 = json_integer(1); + value2 = json_integer(1); + if (!value1 || !value2) + fail("unable to create integers"); + if (!json_equal(value1, value2)) + fail("json_equal fails for two equal integers"); + json_decref(value2); + + value2 = json_integer(2); + if (!value2) + fail("unable to create an integer"); + if (json_equal(value1, value2)) + fail("json_equal fails for two inequal integers"); + + json_decref(value1); + json_decref(value2); + + /* real */ + value1 = json_real(1.2); + value2 = json_real(1.2); + if (!value1 || !value2) + fail("unable to create reals"); + if (!json_equal(value1, value2)) + fail("json_equal fails for two equal reals"); + json_decref(value2); + + value2 = json_real(3.141592); + if (!value2) + fail("unable to create an real"); + if (json_equal(value1, value2)) + fail("json_equal fails for two inequal reals"); + + json_decref(value1); + json_decref(value2); + + /* string */ + value1 = json_string("foo"); + value2 = json_string("foo"); + if (!value1 || !value2) + fail("unable to create strings"); + if (!json_equal(value1, value2)) + fail("json_equal fails for two equal strings"); + json_decref(value2); + + value2 = json_string("bar"); + if (!value2) + fail("unable to create an string"); + if (json_equal(value1, value2)) + fail("json_equal fails for two inequal strings"); + json_decref(value2); + + value2 = json_string("bar2"); + if (!value2) + fail("unable to create an string"); + if (json_equal(value1, value2)) + fail("json_equal fails for two inequal length strings"); + + json_decref(value1); + json_decref(value2); +} + +static void test_equal_array() { + json_t *array1, *array2; + + array1 = json_array(); + array2 = json_array(); + if (!array1 || !array2) + fail("unable to create arrays"); + + if (!json_equal(array1, array2)) + fail("json_equal fails for two empty arrays"); + + json_array_append_new(array1, json_integer(1)); + json_array_append_new(array2, json_integer(1)); + json_array_append_new(array1, json_string("foo")); + json_array_append_new(array2, json_string("foo")); + json_array_append_new(array1, json_integer(2)); + json_array_append_new(array2, json_integer(2)); + if (!json_equal(array1, array2)) + fail("json_equal fails for two equal arrays"); + + json_array_remove(array2, 2); + if (json_equal(array1, array2)) + fail("json_equal fails for two inequal arrays"); + + json_array_append_new(array2, json_integer(3)); + if (json_equal(array1, array2)) + fail("json_equal fails for two inequal arrays"); + + json_decref(array1); + json_decref(array2); +} + +static void test_equal_object() { + json_t *object1, *object2; + + object1 = json_object(); + object2 = json_object(); + if (!object1 || !object2) + fail("unable to create objects"); + + if (!json_equal(object1, object2)) + fail("json_equal fails for two empty objects"); + + json_object_set_new(object1, "a", json_integer(1)); + json_object_set_new(object2, "a", json_integer(1)); + json_object_set_new(object1, "b", json_string("foo")); + json_object_set_new(object2, "b", json_string("foo")); + json_object_set_new(object1, "c", json_integer(2)); + json_object_set_new(object2, "c", json_integer(2)); + if (!json_equal(object1, object2)) + fail("json_equal fails for two equal objects"); + + json_object_del(object2, "c"); + if (json_equal(object1, object2)) + fail("json_equal fails for two inequal objects"); + + json_object_set_new(object2, "c", json_integer(3)); + if (json_equal(object1, object2)) + fail("json_equal fails for two inequal objects"); + + json_object_del(object2, "c"); + json_object_set_new(object2, "d", json_integer(2)); + if (json_equal(object1, object2)) + fail("json_equal fails for two inequal objects"); + + json_decref(object1); + json_decref(object2); +} + +static void test_equal_complex() { + json_t *value1, *value2, *value3; + + const char *complex_json = "{" + " \"integer\": 1, " + " \"real\": 3.141592, " + " \"string\": \"foobar\", " + " \"true\": true, " + " \"object\": {" + " \"array-in-object\": [1,true,\"foo\",{}]," + " \"object-in-object\": {\"foo\": \"bar\"}" + " }," + " \"array\": [\"foo\", false, null, 1.234]" + "}"; + + value1 = json_loads(complex_json, 0, NULL); + value2 = json_loads(complex_json, 0, NULL); + value3 = json_loads(complex_json, 0, NULL); + if (!value1 || !value2) + fail("unable to parse JSON"); + if (!json_equal(value1, value2)) + fail("json_equal fails for two equal objects"); + + json_array_set_new( + json_object_get(json_object_get(value2, "object"), "array-in-object"), 1, + json_false()); + if (json_equal(value1, value2)) + fail("json_equal fails for two inequal objects"); + + json_object_set_new( + json_object_get(json_object_get(value3, "object"), "object-in-object"), "foo", + json_string("baz")); + if (json_equal(value1, value3)) + fail("json_equal fails for two inequal objects"); + + json_decref(value1); + json_decref(value2); + json_decref(value3); +} + +static void run_tests() { + test_equal_simple(); + test_equal_array(); + test_equal_object(); + test_equal_complex(); +} diff --git a/lib/jansson/test/suites/api/test_fixed_size.c b/lib/jansson/test/suites/api/test_fixed_size.c new file mode 100644 index 0000000..4ae9e07 --- /dev/null +++ b/lib/jansson/test/suites/api/test_fixed_size.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2020 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void test_keylen_iterator(json_t *object) { + const char key1[] = {'t', 'e', 's', 't', '1'}; + const char key2[] = {'t', 'e', 's', 't'}; + const char key3[] = {'t', 'e', 's', '\0', 't'}; + const char key4[] = {'t', 'e', 's', 't', '\0'}; + const char *reference_keys[] = {key1, key2, key3, key4}; + const size_t reference_keys_len[] = {sizeof(key1), sizeof(key2), sizeof(key3), + sizeof(key4)}; + size_t index = 0; + json_t *value; + const char *key; + size_t keylen; + + json_object_keylen_foreach(object, key, keylen, value) { + if (keylen != reference_keys_len[index]) + fail("invalid key len in iterator"); + if (memcmp(key, reference_keys[index], reference_keys_len[index]) != 0) + fail("invalid key in iterator"); + + index++; + } +} + +static void test_keylen(void) { + json_t *obj = json_object(); + const char key[] = {'t', 'e', 's', 't', '1'}; + const char key2[] = {'t', 'e', 's', 't'}; + const char key3[] = {'t', 'e', 's', '\0', 't'}; + const char key4[] = {'t', 'e', 's', 't', '\0'}; + + if (json_object_size(obj) != 0) + fail("incorrect json"); + + json_object_set_new_nocheck(obj, "test1", json_true()); + + if (json_object_size(obj) != 1) + fail("incorrect json"); + + if (json_object_getn(obj, key, sizeof(key)) != json_true()) + fail("json_object_getn failed"); + + if (json_object_getn(obj, key2, sizeof(key2)) != NULL) + fail("false positive json_object_getn by key2"); + + if (json_object_setn_nocheck(obj, key2, sizeof(key2), json_false())) + fail("json_object_setn_nocheck for key2 failed"); + + if (json_object_size(obj) != 2) + fail("incorrect json"); + + if (json_object_get(obj, "test") != json_false()) + fail("json_object_setn_nocheck for key2 failed"); + + if (json_object_getn(obj, key2, sizeof(key2)) != json_false()) + fail("json_object_getn by key 2 failed"); + + if (json_object_getn(obj, key3, sizeof(key3)) != NULL) + fail("false positive json_object_getn by key3"); + + if (json_object_setn_nocheck(obj, key3, sizeof(key3), json_false())) + fail("json_object_setn_nocheck for key3 failed"); + + if (json_object_size(obj) != 3) + fail("incorrect json"); + + if (json_object_getn(obj, key3, sizeof(key3)) != json_false()) + fail("json_object_getn by key 3 failed"); + + if (json_object_getn(obj, key4, sizeof(key4)) != NULL) + fail("false positive json_object_getn by key3"); + + if (json_object_setn_nocheck(obj, key4, sizeof(key4), json_false())) + fail("json_object_setn_nocheck for key3 failed"); + + if (json_object_size(obj) != 4) + fail("incorrect json"); + + test_keylen_iterator(obj); + + if (json_object_getn(obj, key4, sizeof(key4)) != json_false()) + fail("json_object_getn by key 3 failed"); + + if (json_object_size(obj) != 4) + fail("incorrect json"); + + if (json_object_deln(obj, key4, sizeof(key4))) + fail("json_object_deln failed"); + if (json_object_getn(obj, key4, sizeof(key4)) != NULL) + fail("json_object_deln failed"); + if (json_object_size(obj) != 3) + fail("incorrect json"); + + if (json_object_deln(obj, key3, sizeof(key3))) + fail("json_object_deln failed"); + if (json_object_getn(obj, key3, sizeof(key3)) != NULL) + fail("json_object_deln failed"); + if (json_object_size(obj) != 2) + fail("incorrect json"); + + if (json_object_deln(obj, key2, sizeof(key2))) + fail("json_object_deln failed"); + if (json_object_getn(obj, key2, sizeof(key2)) != NULL) + fail("json_object_deln failed"); + if (json_object_size(obj) != 1) + fail("incorrect json"); + + if (json_object_deln(obj, key, sizeof(key))) + fail("json_object_deln failed"); + if (json_object_getn(obj, key, sizeof(key)) != NULL) + fail("json_object_deln failed"); + if (json_object_size(obj) != 0) + fail("incorrect json"); + + json_decref(obj); +} + +static void test_invalid_keylen(void) { + json_t *obj = json_object(); + json_t *empty_obj = json_object(); + const char key[] = {'t', 'e', 's', 't', '1'}; + + json_object_set_new_nocheck(obj, "test1", json_true()); + + if (json_object_getn(NULL, key, sizeof(key)) != NULL) + fail("json_object_getn on NULL failed"); + + if (json_object_getn(obj, NULL, sizeof(key)) != NULL) + fail("json_object_getn on NULL failed"); + + if (json_object_getn(obj, key, 0) != NULL) + fail("json_object_getn on NULL failed"); + + if (!json_object_setn_new(obj, NULL, sizeof(key), json_true())) + fail("json_object_setn_new with NULL key failed"); + + if (!json_object_setn_new_nocheck(obj, NULL, sizeof(key), json_true())) + fail("json_object_setn_new_nocheck with NULL key failed"); + + if (!json_object_del(obj, NULL)) + fail("json_object_del with NULL failed"); + + if (!json_object_deln(empty_obj, key, sizeof(key))) + fail("json_object_deln with empty object failed"); + + if (!json_object_deln(obj, key, sizeof(key) - 1)) + fail("json_object_deln with incomplete key failed"); + + json_decref(obj); + json_decref(empty_obj); +} + +static void test_binary_keys(void) { + json_t *obj = json_object(); + int key1 = 0; + int key2 = 1; + + json_object_setn_nocheck(obj, (const char *)&key1, sizeof(key1), json_true()); + json_object_setn_nocheck(obj, (const char *)&key2, sizeof(key2), json_true()); + + if (!json_is_true(json_object_getn(obj, (const char *)&key1, sizeof(key1)))) + fail("cannot get integer key1"); + + if (!json_is_true(json_object_getn(obj, (const char *)&key1, sizeof(key2)))) + fail("cannot get integer key2"); + + if (json_object_size(obj) != 2) + fail("binary object size missmatch"); + + if (json_object_deln(obj, (const char *)&key1, sizeof(key1))) + fail("cannot del integer key1"); + + if (json_object_size(obj) != 1) + fail("binary object size missmatch"); + + if (json_object_deln(obj, (const char *)&key2, sizeof(key2))) + fail("cannot del integer key2"); + + if (json_object_size(obj) != 0) + fail("binary object size missmatch"); + + json_decref(obj); +} + +static void test_dump_order(void) { + json_t *obj = json_object(); + char key1[] = {'k', '\0', '-', '2'}; + char key2[] = {'k', '\0', '-', '1'}; + const char expected_sorted_str[] = + "{\"k\\u0000-1\": \"first\", \"k\\u0000-2\": \"second\"}"; + const char expected_nonsorted_str[] = + "{\"k\\u0000-2\": \"second\", \"k\\u0000-1\": \"first\"}"; + char *out; + + json_object_setn_new_nocheck(obj, key1, sizeof(key1), json_string("second")); + json_object_setn_new_nocheck(obj, key2, sizeof(key2), json_string("first")); + + out = malloc(512); + + json_dumpb(obj, out, 512, 0); + + if (memcmp(expected_nonsorted_str, out, sizeof(expected_nonsorted_str) - 1) != 0) + fail("preserve order failed"); + + json_dumpb(obj, out, 512, JSON_SORT_KEYS); + if (memcmp(expected_sorted_str, out, sizeof(expected_sorted_str) - 1) != 0) + fail("utf-8 sort failed"); + + free(out); + json_decref(obj); +} + +static void run_tests() { + test_keylen(); + test_invalid_keylen(); + test_binary_keys(); + test_dump_order(); +} diff --git a/lib/jansson/test/suites/api/test_load.c b/lib/jansson/test/suites/api/test_load.c new file mode 100644 index 0000000..1c64b0c --- /dev/null +++ b/lib/jansson/test/suites/api/test_load.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void file_not_found() { + json_t *json; + json_error_t error; + char *pos; + + json = json_load_file("/path/to/nonexistent/file.json", 0, &error); + if (json) + fail("json_load_file returned non-NULL for a nonexistent file"); + if (error.line != -1) + fail("json_load_file returned an invalid line number"); + + /* The error message is locale specific, only check the beginning + of the error message. */ + + pos = strchr(error.text, ':'); + if (!pos) + fail("json_load_file returne an invalid error message"); + + *pos = '\0'; + + if (strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0) + fail("json_load_file returned an invalid error message"); + if (json_error_code(&error) != json_error_cannot_open_file) + fail("json_load_file returned an invalid error code"); +} + +static void very_long_file_name() { + json_t *json; + json_error_t error; + + json = json_load_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0, &error); + if (json) + fail("json_load_file returned non-NULL for a nonexistent file"); + if (error.line != -1) + fail("json_load_file returned an invalid line number"); + + if (strncmp(error.source, "...aaa", 6) != 0) + fail("error source was set incorrectly"); + if (json_error_code(&error) != json_error_cannot_open_file) + fail("error code was set incorrectly"); +} + +static void reject_duplicates() { + json_error_t error; + + if (json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error)) + fail("json_loads did not detect a duplicate key"); + check_error(json_error_duplicate_key, "duplicate object key near '\"foo\"'", + "", 1, 16, 16); +} + +static void disable_eof_check() { + json_error_t error; + json_t *json; + + const char *text = "{\"foo\": 1} garbage"; + + if (json_loads(text, 0, &error)) + fail("json_loads did not detect garbage after JSON text"); + check_error(json_error_end_of_input_expected, "end of file expected near 'garbage'", + "", 1, 18, 18); + + json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error); + if (!json) + fail("json_loads failed with JSON_DISABLE_EOF_CHECK"); + + json_decref(json); +} + +static void decode_any() { + json_t *json; + json_error_t error; + + json = json_loads("\"foo\"", JSON_DECODE_ANY, &error); + if (!json || !json_is_string(json)) + fail("json_load decoded any failed - string"); + json_decref(json); + + json = json_loads("42", JSON_DECODE_ANY, &error); + if (!json || !json_is_integer(json)) + fail("json_load decoded any failed - integer"); + json_decref(json); + + json = json_loads("true", JSON_DECODE_ANY, &error); + if (!json || !json_is_true(json)) + fail("json_load decoded any failed - boolean"); + json_decref(json); + + json = json_loads("null", JSON_DECODE_ANY, &error); + if (!json || !json_is_null(json)) + fail("json_load decoded any failed - null"); + json_decref(json); +} + +static void decode_int_as_real() { + json_t *json; + json_error_t error; + +#if JSON_INTEGER_IS_LONG_LONG + const char *imprecise; + json_int_t expected; +#endif + + char big[311]; + + json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); + if (!json || !json_is_real(json) || json_real_value(json) != 42.0) + fail("json_load decode int as real failed - int"); + json_decref(json); + +#if JSON_INTEGER_IS_LONG_LONG + /* This number cannot be represented exactly by a double */ + imprecise = "9007199254740993"; + expected = 9007199254740992ll; + + json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); + if (!json || !json_is_real(json) || expected != (json_int_t)json_real_value(json)) + fail("json_load decode int as real failed - expected imprecision"); + json_decref(json); +#endif + + /* 1E309 overflows. Here we create 1E309 as a decimal number, i.e. + 1000...(309 zeroes)...0. */ + big[0] = '1'; + memset(big + 1, '0', 309); + big[310] = '\0'; + + json = json_loads(big, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); + if (json || strcmp(error.text, "real number overflow") != 0 || + json_error_code(&error) != json_error_numeric_overflow) + fail("json_load decode int as real failed - expected overflow"); + json_decref(json); +} + +static void allow_nul() { + const char *text = "\"nul byte \\u0000 in string\""; + const char *expected = "nul byte \0 in string"; + size_t len = 20; + json_t *json; + + json = json_loads(text, JSON_ALLOW_NUL | JSON_DECODE_ANY, NULL); + if (!json || !json_is_string(json)) + fail("unable to decode embedded NUL byte"); + + if (json_string_length(json) != len) + fail("decoder returned wrong string length"); + + if (memcmp(json_string_value(json), expected, len + 1)) + fail("decoder returned wrong string content"); + + json_decref(json); +} + +static void load_wrong_args() { + json_t *json; + json_error_t error; + + json = json_loads(NULL, 0, &error); + if (json) + fail("json_loads should return NULL if the first argument is NULL"); + + json = json_loadb(NULL, 0, 0, &error); + if (json) + fail("json_loadb should return NULL if the first argument is NULL"); + + json = json_loadf(NULL, 0, &error); + if (json) + fail("json_loadf should return NULL if the first argument is NULL"); + + json = json_loadfd(-1, 0, &error); + if (json) + fail("json_loadfd should return NULL if the first argument is < 0"); + + json = json_load_file(NULL, 0, &error); + if (json) + fail("json_load_file should return NULL if the first argument is NULL"); +} + +static void position() { + json_t *json; + size_t flags = JSON_DISABLE_EOF_CHECK; + json_error_t error; + + json = json_loads("{\"foo\": \"bar\"}", 0, &error); + if (error.position != 14) + fail("json_loads returned a wrong position"); + json_decref(json); + + json = json_loads("{\"foo\": \"bar\"} baz quux", flags, &error); + if (error.position != 14) + fail("json_loads returned a wrong position"); + json_decref(json); +} + +static void error_code() { + json_error_t error; + json_t *json = json_loads("[123] garbage", 0, &error); + if (json != NULL) + fail("json_loads returned not NULL"); + if (strlen(error.text) >= JSON_ERROR_TEXT_LENGTH) + fail("error.text longer than expected"); + if (json_error_code(&error) != json_error_end_of_input_expected) + fail("json_loads returned incorrect error code"); + + json = json_loads("{\"foo\": ", 0, &error); + if (json != NULL) + fail("json_loads returned not NULL"); + if (strlen(error.text) >= JSON_ERROR_TEXT_LENGTH) + fail("error.text longer than expected"); + if (json_error_code(&error) != json_error_premature_end_of_input) + fail("json_loads returned incorrect error code"); +} + +static void run_tests() { + file_not_found(); + very_long_file_name(); + reject_duplicates(); + disable_eof_check(); + decode_any(); + decode_int_as_real(); + allow_nul(); + load_wrong_args(); + position(); + error_code(); +} diff --git a/lib/jansson/test/suites/api/test_load_callback.c b/lib/jansson/test/suites/api/test_load_callback.c new file mode 100644 index 0000000..b292fcf --- /dev/null +++ b/lib/jansson/test/suites/api/test_load_callback.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2009-2011 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include +#include + +struct my_source { + const char *buf; + size_t off; + size_t cap; +}; + +static const char my_str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]"; + +static size_t greedy_reader(void *buf, size_t buflen, void *arg) { + struct my_source *s = arg; + if (buflen > s->cap - s->off) + buflen = s->cap - s->off; + if (buflen > 0) { + memcpy(buf, s->buf + s->off, buflen); + s->off += buflen; + return buflen; + } else { + return 0; + } +} + +static void run_tests() { + struct my_source s; + json_t *json; + json_error_t error; + + s.off = 0; + s.cap = strlen(my_str); + s.buf = my_str; + + json = json_load_callback(greedy_reader, &s, 0, &error); + + if (!json) + fail("json_load_callback failed on a valid callback"); + json_decref(json); + + s.off = 0; + s.cap = strlen(my_str) - 1; + s.buf = my_str; + + json = json_load_callback(greedy_reader, &s, 0, &error); + if (json) { + json_decref(json); + fail("json_load_callback should have failed on an incomplete stream, " + "but it didn't"); + } + if (strcmp(error.source, "") != 0) { + fail("json_load_callback returned an invalid error source"); + } + if (strcmp(error.text, "']' expected near end of file") != 0) { + fail("json_load_callback returned an invalid error message for an " + "unclosed top-level array"); + } + + json = json_load_callback(NULL, NULL, 0, &error); + if (json) { + json_decref(json); + fail("json_load_callback should have failed on NULL load callback, but " + "it didn't"); + } + if (strcmp(error.text, "wrong arguments") != 0) { + fail("json_load_callback returned an invalid error message for a NULL " + "load callback"); + } +} diff --git a/lib/jansson/test/suites/api/test_loadb.c b/lib/jansson/test/suites/api/test_loadb.c new file mode 100644 index 0000000..2cd88fe --- /dev/null +++ b/lib/jansson/test/suites/api/test_loadb.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void run_tests() { + json_t *json; + json_error_t error; + const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage"; + size_t len = strlen(str) - strlen("garbage"); + + json = json_loadb(str, len, 0, &error); + if (!json) { + fail("json_loadb failed on a valid JSON buffer"); + } + json_decref(json); + + json = json_loadb(str, len - 1, 0, &error); + if (json) { + json_decref(json); + fail("json_loadb should have failed on an incomplete buffer, but it " + "didn't"); + } + if (error.line != 1) { + fail("json_loadb returned an invalid line number on fail"); + } + if (strcmp(error.text, "']' expected near end of file") != 0) { + fail("json_loadb returned an invalid error message for an unclosed " + "top-level array"); + } +} diff --git a/lib/jansson/test/suites/api/test_memory_funcs.c b/lib/jansson/test/suites/api/test_memory_funcs.c new file mode 100644 index 0000000..4fd6166 --- /dev/null +++ b/lib/jansson/test/suites/api/test_memory_funcs.c @@ -0,0 +1,115 @@ +#include +#include + +#include "util.h" + +static int malloc_called = 0; +static int free_called = 0; +static size_t malloc_used = 0; + +/* helpers */ +static void create_and_free_complex_object() { + json_t *obj; + + obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]}", "foo", 42, "bar", "baz", 1, + "qux", 0, "alice", "bar", "baz", "bob", 9, 8, 7); + + json_decref(obj); +} + +static void create_and_free_object_with_oom() { + int i; + char key[4]; + json_t *obj = json_object(); + + for (i = 0; i < 10; i++) { + snprintf(key, sizeof key, "%d", i); + json_object_set_new(obj, key, json_integer(i)); + } + + json_decref(obj); +} + +static void *my_malloc(size_t size) { + malloc_called = 1; + return malloc(size); +} + +static void my_free(void *ptr) { + free_called = 1; + free(ptr); +} + +static void test_simple() { + json_malloc_t mfunc = NULL; + json_free_t ffunc = NULL; + + json_set_alloc_funcs(my_malloc, my_free); + json_get_alloc_funcs(&mfunc, &ffunc); + create_and_free_complex_object(); + + if (malloc_called != 1 || free_called != 1 || mfunc != my_malloc || ffunc != my_free) + fail("Custom allocation failed"); +} + +static void *oom_malloc(size_t size) { + if (malloc_used + size > 800) + return NULL; + + malloc_used += size; + return malloc(size); +} + +static void oom_free(void *ptr) { + free_called++; + free(ptr); +} + +static void test_oom() { + free_called = 0; + json_set_alloc_funcs(oom_malloc, oom_free); + create_and_free_object_with_oom(); + + if (free_called == 0) + fail("Allocation with OOM failed"); +} + +/* + Test the secure memory functions code given in the API reference + documentation, but by using plain memset instead of + guaranteed_memset(). +*/ + +static void *secure_malloc(size_t size) { + /* Store the memory area size in the beginning of the block */ + void *ptr = malloc(size + 8); + *((size_t *)ptr) = size; + return (char *)ptr + 8; +} + +static void secure_free(void *ptr) { + size_t size; + + ptr = (char *)ptr - 8; + size = *((size_t *)ptr); + + /*guaranteed_*/ memset(ptr, 0, size + 8); + free(ptr); +} + +static void test_secure_funcs(void) { + json_set_alloc_funcs(secure_malloc, secure_free); + create_and_free_complex_object(); +} + +static void test_bad_args(void) { + /* The result of this test is not crashing. */ + json_get_alloc_funcs(NULL, NULL); +} + +static void run_tests() { + test_simple(); + test_secure_funcs(); + test_oom(); + test_bad_args(); +} diff --git a/lib/jansson/test/suites/api/test_number.c b/lib/jansson/test/suites/api/test_number.c new file mode 100644 index 0000000..2a22a67 --- /dev/null +++ b/lib/jansson/test/suites/api/test_number.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +#ifdef INFINITY +// This test triggers "warning C4756: overflow in constant arithmetic" +// in Visual Studio. This warning is triggered here by design, so disable it. +// (This can only be done on function level so we keep these tests separate) +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4756) +#endif +static void test_inifity() { + json_t *real = json_real(INFINITY); + if (real != NULL) + fail("could construct a real from Inf"); + + real = json_real(1.0); + if (json_real_set(real, INFINITY) != -1) + fail("could set a real to Inf"); + + if (json_real_value(real) != 1.0) + fail("real value changed unexpectedly"); + + json_decref(real); +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} +#endif // INFINITY + +static void test_bad_args(void) { + json_t *txt = json_string("test"); + + if (json_integer_value(NULL) != 0) + fail("json_integer_value did not return 0 for non-integer"); + if (json_integer_value(txt) != 0) + fail("json_integer_value did not return 0 for non-integer"); + + if (!json_integer_set(NULL, 0)) + fail("json_integer_set did not return error for non-integer"); + if (!json_integer_set(txt, 0)) + fail("json_integer_set did not return error for non-integer"); + + if (json_real_value(NULL) != 0.0) + fail("json_real_value did not return 0.0 for non-real"); + if (json_real_value(txt) != 0.0) + fail("json_real_value did not return 0.0 for non-real"); + + if (!json_real_set(NULL, 0.0)) + fail("json_real_set did not return error for non-real"); + if (!json_real_set(txt, 0.0)) + fail("json_real_set did not return error for non-real"); + + if (json_number_value(NULL) != 0.0) + fail("json_number_value did not return 0.0 for non-numeric"); + if (json_number_value(txt) != 0.0) + fail("json_number_value did not return 0.0 for non-numeric"); + + if (txt->refcount != 1) + fail("unexpected reference count for txt"); + + json_decref(txt); +} + +static void run_tests() { + json_t *integer, *real; + json_int_t i; + double d; + + integer = json_integer(5); + real = json_real(100.1); + + if (!integer) + fail("unable to create integer"); + if (!real) + fail("unable to create real"); + + i = json_integer_value(integer); + if (i != 5) + fail("wrong integer value"); + + d = json_real_value(real); + if (d != 100.1) + fail("wrong real value"); + + d = json_number_value(integer); + if (d != 5.0) + fail("wrong number value"); + d = json_number_value(real); + if (d != 100.1) + fail("wrong number value"); + + json_decref(integer); + json_decref(real); + +#ifdef NAN + real = json_real(NAN); + if (real != NULL) + fail("could construct a real from NaN"); + + real = json_real(1.0); + if (json_real_set(real, NAN) != -1) + fail("could set a real to NaN"); + + if (json_real_value(real) != 1.0) + fail("real value changed unexpectedly"); + + json_decref(real); +#endif + +#ifdef INFINITY + test_inifity(); +#endif + test_bad_args(); +} diff --git a/lib/jansson/test/suites/api/test_object.c b/lib/jansson/test/suites/api/test_object.c new file mode 100644 index 0000000..331edf2 --- /dev/null +++ b/lib/jansson/test/suites/api/test_object.c @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void test_clear() { + json_t *object, *ten; + + object = json_object(); + ten = json_integer(10); + + if (!object) + fail("unable to create object"); + if (!ten) + fail("unable to create integer"); + + if (json_object_set(object, "a", ten) || json_object_set(object, "b", ten) || + json_object_set(object, "c", ten) || json_object_set(object, "d", ten) || + json_object_set(object, "e", ten)) + fail("unable to set value"); + + if (json_object_size(object) != 5) + fail("invalid size"); + + json_object_clear(object); + + if (json_object_size(object) != 0) + fail("invalid size after clear"); + + json_decref(ten); + json_decref(object); +} + +static void test_update() { + json_t *object, *other, *nine, *ten; + + object = json_object(); + other = json_object(); + + nine = json_integer(9); + ten = json_integer(10); + + if (!object || !other) + fail("unable to create object"); + if (!nine || !ten) + fail("unable to create integer"); + + /* update an empty object with an empty object */ + + if (json_object_update(object, other)) + fail("unable to update an empty object with an empty object"); + + if (json_object_size(object) != 0) + fail("invalid size after update"); + + if (json_object_size(other) != 0) + fail("invalid size for updater after update"); + + /* update an empty object with a nonempty object */ + + if (json_object_set(other, "a", ten) || json_object_set(other, "b", ten) || + json_object_set(other, "c", ten) || json_object_set(other, "d", ten) || + json_object_set(other, "e", ten)) + fail("unable to set value"); + + if (json_object_update(object, other)) + fail("unable to update an empty object"); + + if (json_object_size(object) != 5) + fail("invalid size after update"); + + if (json_object_get(object, "a") != ten || json_object_get(object, "b") != ten || + json_object_get(object, "c") != ten || json_object_get(object, "d") != ten || + json_object_get(object, "e") != ten) + fail("update works incorrectly"); + + /* perform the same update again */ + + if (json_object_update(object, other)) + fail("unable to update a non-empty object"); + + if (json_object_size(object) != 5) + fail("invalid size after update"); + + if (json_object_get(object, "a") != ten || json_object_get(object, "b") != ten || + json_object_get(object, "c") != ten || json_object_get(object, "d") != ten || + json_object_get(object, "e") != ten) + fail("update works incorrectly"); + + /* update a nonempty object with a nonempty object with both old + and new keys */ + + if (json_object_clear(other)) + fail("clear failed"); + + if (json_object_set(other, "a", nine) || json_object_set(other, "b", nine) || + json_object_set(other, "f", nine) || json_object_set(other, "g", nine) || + json_object_set(other, "h", nine)) + fail("unable to set value"); + + if (json_object_update(object, other)) + fail("unable to update a nonempty object"); + + if (json_object_size(object) != 8) + fail("invalid size after update"); + + if (json_object_get(object, "a") != nine || json_object_get(object, "b") != nine || + json_object_get(object, "f") != nine || json_object_get(object, "g") != nine || + json_object_get(object, "h") != nine) + fail("update works incorrectly"); + + /* update_new check */ + if (json_object_clear(object)) + fail("clear failed"); + + if (json_object_set(object, "a", ten) || json_object_set(object, "b", ten) || + json_object_set(object, "c", ten) || json_object_set(object, "d", ten) || + json_object_set(object, "e", ten)) + fail("unable to set value"); + + if (json_object_update_new( + object, json_pack("{s:O, s:O, s:O}", "b", nine, "f", nine, "g", nine))) + fail("unable to update_new a nonempty object"); + + if (json_object_size(object) != 7) + fail("invalid size after update_new"); + + if (json_object_get(object, "a") != ten || json_object_get(object, "b") != nine || + json_object_get(object, "c") != ten || json_object_get(object, "d") != ten || + json_object_get(object, "e") != ten || json_object_get(object, "f") != nine || + json_object_get(object, "g") != nine) + fail("update_new works incorrectly"); + + json_decref(nine); + json_decref(ten); + json_decref(other); + json_decref(object); +} + +static void test_set_many_keys() { + json_t *object, *value; + const char *keys = "abcdefghijklmnopqrstuvwxyz"; + char buf[2]; + size_t i; + + object = json_object(); + if (!object) + fail("unable to create object"); + + value = json_string("a"); + if (!value) + fail("unable to create string"); + + buf[1] = '\0'; + for (i = 0; i < strlen(keys); i++) { + buf[0] = keys[i]; + if (json_object_set(object, buf, value)) + fail("unable to set object key"); + } + + json_decref(object); + json_decref(value); +} + +static void test_conditional_updates() { + json_t *object, *other; + + object = json_pack("{sisi}", "foo", 1, "bar", 2); + other = json_pack("{sisi}", "foo", 3, "baz", 4); + + if (json_object_update_existing(object, other)) + fail("json_object_update_existing failed"); + + if (json_object_size(object) != 2) + fail("json_object_update_existing added new items"); + + if (json_integer_value(json_object_get(object, "foo")) != 3) + fail("json_object_update_existing failed to update existing key"); + + if (json_integer_value(json_object_get(object, "bar")) != 2) + fail("json_object_update_existing updated wrong key"); + + json_decref(object); + + /* json_object_update_existing_new check */ + object = json_pack("{sisi}", "foo", 1, "bar", 2); + + if (json_object_update_existing_new(object, json_pack("{sisi}", "foo", 3, "baz", 4))) + fail("json_object_update_existing_new failed"); + + if (json_object_size(object) != 2) + fail("json_object_update_existing_new added new items"); + + if (json_integer_value(json_object_get(object, "foo")) != 3) + fail("json_object_update_existing_new failed to update existing key"); + + if (json_integer_value(json_object_get(object, "bar")) != 2) + fail("json_object_update_existing_new updated wrong key"); + + json_decref(object); + + object = json_pack("{sisi}", "foo", 1, "bar", 2); + + if (json_object_update_missing(object, other)) + fail("json_object_update_missing failed"); + + if (json_object_size(object) != 3) + fail("json_object_update_missing didn't add new items"); + + if (json_integer_value(json_object_get(object, "foo")) != 1) + fail("json_object_update_missing updated existing key"); + + if (json_integer_value(json_object_get(object, "bar")) != 2) + fail("json_object_update_missing updated wrong key"); + + if (json_integer_value(json_object_get(object, "baz")) != 4) + fail("json_object_update_missing didn't add new items"); + + json_decref(object); + + /* json_object_update_missing_new check */ + object = json_pack("{sisi}", "foo", 1, "bar", 2); + + if (json_object_update_missing_new(object, json_pack("{sisi}", "foo", 3, "baz", 4))) + fail("json_object_update_missing_new failed"); + + if (json_object_size(object) != 3) + fail("json_object_update_missing_new didn't add new items"); + + if (json_integer_value(json_object_get(object, "foo")) != 1) + fail("json_object_update_missing_new updated existing key"); + + if (json_integer_value(json_object_get(object, "bar")) != 2) + fail("json_object_update_missing_new updated wrong key"); + + if (json_integer_value(json_object_get(object, "baz")) != 4) + fail("json_object_update_missing_new didn't add new items"); + + json_decref(object); + json_decref(other); +} + +static void test_recursive_updates() { + json_t *invalid, *object, *other, *barBefore, *barAfter; + + invalid = json_integer(42); + + object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2); + other = json_pack("{sisisi}", "foo", 3, "bar", 4, "baz", 5); + + if (!json_object_update_recursive(invalid, other)) + fail("json_object_update_recursive accepted non-object argument"); + + json_decref(invalid); + + if (json_object_update_recursive(object, other)) + fail("json_object_update_recursive failed"); + + if (json_object_size(object) != 3) + fail("invalid size after update"); + + if (json_integer_value(json_object_get(object, "foo")) != 3) + fail("json_object_update_recursive failed to update existing key"); + + if (json_integer_value(json_object_get(object, "bar")) != 4) + fail("json_object_update_recursive failed to overwrite object"); + + if (json_integer_value(json_object_get(object, "baz")) != 5) + fail("json_object_update_recursive didn't add new item"); + + json_decref(object); + json_decref(other); + + object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2); + other = json_pack("{s{si}}", "bar", "baz", 3); + barBefore = json_object_get(object, "bar"); + + if (!barBefore) + fail("can't get bar object before json_object_update_recursive"); + + if (json_object_update_recursive(object, other)) + fail("json_object_update_recursive failed"); + + if (json_object_size(object) != 2) + fail("invalid size after update"); + + if (!json_object_get(object, "foo")) + fail("json_object_update_recursive removed existing key"); + + if (json_integer_value(json_object_get(json_object_get(object, "bar"), "baz")) != 3) + fail("json_object_update_recursive failed to update nested value"); + + barAfter = json_object_get(object, "bar"); + if (!barAfter) + fail("can't get bar object after json_object_update_recursive"); + + if (barBefore != barAfter) + fail("bar object reference changed after json_object_update_recursive"); + + json_decref(object); + json_decref(other); + + /* check circular reference */ + object = json_pack("{s{s{s{si}}}}", "foo", "bar", "baz", "xxx", 2); + other = json_pack("{s{s{si}}}", "foo", "bar", "baz", 2); + json_object_set(json_object_get(json_object_get(other, "foo"), "bar"), "baz", + json_object_get(other, "foo")); + + if (!json_object_update_recursive(object, other)) + fail("json_object_update_recursive update a circular reference!"); + + json_object_set_new(json_object_get(json_object_get(other, "foo"), "bar"), "baz", + json_integer(1)); + + if (json_object_update_recursive(object, other)) + fail("json_object_update_recursive failed!"); + + json_decref(object); + json_decref(other); +} + +static void test_circular() { + json_t *object1, *object2; + + object1 = json_object(); + object2 = json_object(); + if (!object1 || !object2) + fail("unable to create object"); + + /* the simple case is checked */ + if (json_object_set(object1, "a", object1) == 0) + fail("able to set self"); + + /* create circular references */ + if (json_object_set(object1, "a", object2) || json_object_set(object2, "a", object1)) + fail("unable to set value"); + + /* circularity is detected when dumping */ + if (json_dumps(object1, 0) != NULL) + fail("able to dump circulars"); + + /* decref twice to deal with the circular references */ + json_decref(object1); + json_decref(object2); + json_decref(object1); +} + +static void test_set_nocheck() { + json_t *object, *string; + + object = json_object(); + string = json_string("bar"); + + if (!object) + fail("unable to create object"); + if (!string) + fail("unable to create string"); + + if (json_object_set_nocheck(object, "foo", string)) + fail("json_object_set_nocheck failed"); + if (json_object_get(object, "foo") != string) + fail("json_object_get after json_object_set_nocheck failed"); + + /* invalid UTF-8 in key */ + if (json_object_set_nocheck(object, "a\xefz", string)) + fail("json_object_set_nocheck failed for invalid UTF-8"); + if (json_object_get(object, "a\xefz") != string) + fail("json_object_get after json_object_set_nocheck failed"); + + if (json_object_set_new_nocheck(object, "bax", json_integer(123))) + fail("json_object_set_new_nocheck failed"); + if (json_integer_value(json_object_get(object, "bax")) != 123) + fail("json_object_get after json_object_set_new_nocheck failed"); + + /* invalid UTF-8 in key */ + if (json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321))) + fail("json_object_set_new_nocheck failed for invalid UTF-8"); + if (json_integer_value(json_object_get(object, "asdf\xfe")) != 321) + fail("json_object_get after json_object_set_new_nocheck failed"); + + json_decref(string); + json_decref(object); +} + +static void test_iterators() { + json_t *object, *foo, *bar, *baz; + void *iter; + + if (json_object_iter(NULL)) + fail("able to iterate over NULL"); + + if (json_object_iter_next(NULL, NULL)) + fail("able to increment an iterator on a NULL object"); + + object = json_object(); + foo = json_string("foo"); + bar = json_string("bar"); + baz = json_string("baz"); + if (!object || !foo || !bar || !baz) + fail("unable to create values"); + + if (json_object_iter_next(object, NULL)) + fail("able to increment a NULL iterator"); + + if (json_object_set(object, "a", foo) || json_object_set(object, "b", bar) || + json_object_set(object, "c", baz)) + fail("unable to populate object"); + + iter = json_object_iter(object); + if (!iter) + fail("unable to get iterator"); + if (strcmp(json_object_iter_key(iter), "a") != 0) + fail("iterating doesn't yield keys in order"); + if (json_object_iter_value(iter) != foo) + fail("iterating doesn't yield values in order"); + + iter = json_object_iter_next(object, iter); + if (!iter) + fail("unable to increment iterator"); + if (strcmp(json_object_iter_key(iter), "b") != 0) + fail("iterating doesn't yield keys in order"); + if (json_object_iter_value(iter) != bar) + fail("iterating doesn't yield values in order"); + + iter = json_object_iter_next(object, iter); + if (!iter) + fail("unable to increment iterator"); + if (strcmp(json_object_iter_key(iter), "c") != 0) + fail("iterating doesn't yield keys in order"); + if (json_object_iter_value(iter) != baz) + fail("iterating doesn't yield values in order"); + + if (json_object_iter_next(object, iter) != NULL) + fail("able to iterate over the end"); + + if (json_object_iter_at(object, "foo")) + fail("json_object_iter_at() succeeds for non-existent key"); + + iter = json_object_iter_at(object, "b"); + if (!iter) + fail("json_object_iter_at() fails for an existing key"); + + if (strcmp(json_object_iter_key(iter), "b")) + fail("iterating failed: wrong key"); + if (json_object_iter_value(iter) != bar) + fail("iterating failed: wrong value"); + + if (json_object_iter_set(object, iter, baz)) + fail("unable to set value at iterator"); + + if (strcmp(json_object_iter_key(iter), "b")) + fail("json_object_iter_key() fails after json_object_iter_set()"); + if (json_object_iter_value(iter) != baz) + fail("json_object_iter_value() fails after json_object_iter_set()"); + if (json_object_get(object, "b") != baz) + fail("json_object_get() fails after json_object_iter_set()"); + + json_decref(object); + json_decref(foo); + json_decref(bar); + json_decref(baz); +} + +static void test_misc() { + json_t *object, *string, *other_string, *value; + + object = json_object(); + string = json_string("test"); + other_string = json_string("other"); + + if (!object) + fail("unable to create object"); + if (!string || !other_string) + fail("unable to create string"); + + if (json_object_get(object, "a")) + fail("value for nonexisting key"); + + if (json_object_set(object, "a", string)) + fail("unable to set value"); + + if (!json_object_set(object, NULL, string)) + fail("able to set NULL key"); + + if (json_object_del(object, "a")) + fail("unable to del the only key"); + + if (json_object_set(object, "a", string)) + fail("unable to set value"); + + if (!json_object_set(object, "a", NULL)) + fail("able to set NULL value"); + + /* invalid UTF-8 in key */ + if (!json_object_set(object, "a\xefz", string)) + fail("able to set invalid unicode key"); + + value = json_object_get(object, "a"); + if (!value) + fail("no value for existing key"); + if (value != string) + fail("got different value than what was added"); + + /* "a", "lp" and "px" collide in a five-bucket hashtable */ + if (json_object_set(object, "b", string) || json_object_set(object, "lp", string) || + json_object_set(object, "px", string)) + fail("unable to set value"); + + value = json_object_get(object, "a"); + if (!value) + fail("no value for existing key"); + if (value != string) + fail("got different value than what was added"); + + if (json_object_set(object, "a", other_string)) + fail("unable to replace an existing key"); + + value = json_object_get(object, "a"); + if (!value) + fail("no value for existing key"); + if (value != other_string) + fail("got different value than what was set"); + + if (!json_object_del(object, "nonexisting")) + fail("able to delete a nonexisting key"); + + if (json_object_del(object, "px")) + fail("unable to delete an existing key"); + + if (json_object_del(object, "a")) + fail("unable to delete an existing key"); + + if (json_object_del(object, "lp")) + fail("unable to delete an existing key"); + + /* add many keys to initiate rehashing */ + + if (json_object_set(object, "a", string)) + fail("unable to set value"); + + if (json_object_set(object, "lp", string)) + fail("unable to set value"); + + if (json_object_set(object, "px", string)) + fail("unable to set value"); + + if (json_object_set(object, "c", string)) + fail("unable to set value"); + + if (json_object_set(object, "d", string)) + fail("unable to set value"); + + if (json_object_set(object, "e", string)) + fail("unable to set value"); + + if (json_object_set_new(object, "foo", json_integer(123))) + fail("unable to set new value"); + + value = json_object_get(object, "foo"); + if (!json_is_integer(value) || json_integer_value(value) != 123) + fail("json_object_set_new works incorrectly"); + + if (!json_object_set_new(object, NULL, json_integer(432))) + fail("able to set_new NULL key"); + + if (!json_object_set_new(object, "foo", NULL)) + fail("able to set_new NULL value"); + + json_decref(string); + json_decref(other_string); + json_decref(object); +} + +static void test_preserve_order() { + json_t *object; + char *result; + + const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": " + "3, \"sit amet\": 5, \"helicopter\": 7}"; + + object = json_object(); + + json_object_set_new(object, "foobar", json_integer(1)); + json_object_set_new(object, "bazquux", json_integer(2)); + json_object_set_new(object, "lorem ipsum", json_integer(3)); + json_object_set_new(object, "dolor", json_integer(4)); + json_object_set_new(object, "sit amet", json_integer(5)); + + /* changing a value should preserve the order */ + json_object_set_new(object, "bazquux", json_integer(6)); + + /* deletion shouldn't change the order of others */ + json_object_del(object, "dolor"); + + /* add a new item just to make sure */ + json_object_set_new(object, "helicopter", json_integer(7)); + + result = json_dumps(object, JSON_PRESERVE_ORDER); + + if (strcmp(expected, result) != 0) { + fprintf(stderr, "%s != %s", expected, result); + fail("JSON_PRESERVE_ORDER doesn't work"); + } + + free(result); + json_decref(object); +} + +static void test_object_foreach() { + const char *key; + json_t *object1, *object2, *value; + + object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3); + object2 = json_object(); + + json_object_foreach(object1, key, value) json_object_set(object2, key, value); + + if (!json_equal(object1, object2)) + fail("json_object_foreach failed to iterate all key-value pairs"); + + json_decref(object1); + json_decref(object2); +} + +static void test_object_foreach_safe() { + const char *key; + void *tmp; + json_t *object, *value; + + object = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3); + + json_object_foreach_safe(object, tmp, key, value) { json_object_del(object, key); } + + if (json_object_size(object) != 0) + fail("json_object_foreach_safe failed to iterate all key-value pairs"); + + json_decref(object); +} + +static void test_bad_args(void) { + json_t *obj = json_object(); + json_t *num = json_integer(1); + void *iter; + + if (!obj || !num) + fail("failed to allocate test objects"); + + if (json_object_set(obj, "testkey", json_null())) + fail("failed to set testkey on object"); + + iter = json_object_iter(obj); + if (!iter) + fail("failed to retrieve test iterator"); + + if (json_object_size(NULL) != 0) + fail("json_object_size with non-object argument returned non-zero"); + if (json_object_size(num) != 0) + fail("json_object_size with non-object argument returned non-zero"); + + if (json_object_get(NULL, "test") != NULL) + fail("json_object_get with non-object argument returned non-NULL"); + if (json_object_get(num, "test") != NULL) + fail("json_object_get with non-object argument returned non-NULL"); + if (json_object_get(obj, NULL) != NULL) + fail("json_object_get with NULL key returned non-NULL"); + + if (!json_object_set_new_nocheck(NULL, "test", json_null())) + fail("json_object_set_new_nocheck with non-object argument did not " + "return error"); + if (!json_object_set_new_nocheck(num, "test", json_null())) + fail("json_object_set_new_nocheck with non-object argument did not " + "return error"); + if (!json_object_set_new_nocheck(obj, "test", json_incref(obj))) + fail("json_object_set_new_nocheck with object == value did not return " + "error"); + if (!json_object_set_new_nocheck(obj, NULL, json_object())) + fail("json_object_set_new_nocheck with NULL key did not return error"); + + if (!json_object_del(NULL, "test")) + fail("json_object_del with non-object argument did not return error"); + if (!json_object_del(num, "test")) + fail("json_object_del with non-object argument did not return error"); + if (!json_object_del(obj, NULL)) + fail("json_object_del with NULL key did not return error"); + + if (!json_object_clear(NULL)) + fail("json_object_clear with non-object argument did not return error"); + if (!json_object_clear(num)) + fail("json_object_clear with non-object argument did not return error"); + + if (!json_object_update(NULL, obj)) + fail("json_object_update with non-object first argument did not return " + "error"); + if (!json_object_update(num, obj)) + fail("json_object_update with non-object first argument did not return " + "error"); + if (!json_object_update(obj, NULL)) + fail("json_object_update with non-object second argument did not " + "return error"); + if (!json_object_update(obj, num)) + fail("json_object_update with non-object second argument did not " + "return error"); + + if (!json_object_update_existing(NULL, obj)) + fail("json_object_update_existing with non-object first argument did " + "not return error"); + if (!json_object_update_existing(num, obj)) + fail("json_object_update_existing with non-object first argument did " + "not return error"); + if (!json_object_update_existing(obj, NULL)) + fail("json_object_update_existing with non-object second argument did " + "not return error"); + if (!json_object_update_existing(obj, num)) + fail("json_object_update_existing with non-object second argument did " + "not return error"); + + if (!json_object_update_missing(NULL, obj)) + fail("json_object_update_missing with non-object first argument did " + "not return error"); + if (!json_object_update_missing(num, obj)) + fail("json_object_update_missing with non-object first argument did " + "not return error"); + if (!json_object_update_missing(obj, NULL)) + fail("json_object_update_missing with non-object second argument did " + "not return error"); + if (!json_object_update_missing(obj, num)) + fail("json_object_update_missing with non-object second argument did " + "not return error"); + + if (json_object_iter(NULL) != NULL) + fail("json_object_iter with non-object argument returned non-NULL"); + if (json_object_iter(num) != NULL) + fail("json_object_iter with non-object argument returned non-NULL"); + + if (json_object_iter_at(NULL, "test") != NULL) + fail("json_object_iter_at with non-object argument returned non-NULL"); + if (json_object_iter_at(num, "test") != NULL) + fail("json_object_iter_at with non-object argument returned non-NULL"); + if (json_object_iter_at(obj, NULL) != NULL) + fail("json_object_iter_at with NULL iter returned non-NULL"); + + if (json_object_iter_next(obj, NULL) != NULL) + fail("json_object_iter_next with NULL iter returned non-NULL"); + if (json_object_iter_next(num, iter) != NULL) + fail("json_object_iter_next with non-object argument returned non-NULL"); + + if (json_object_iter_key(NULL) != NULL) + fail("json_object_iter_key with NULL iter returned non-NULL"); + + if (json_object_key_to_iter(NULL) != NULL) + fail("json_object_key_to_iter with NULL iter returned non-NULL"); + + if (json_object_iter_value(NULL) != NULL) + fail("json_object_iter_value with NULL iter returned non-NULL"); + + if (!json_object_iter_set_new(NULL, iter, json_incref(num))) + fail("json_object_iter_set_new with non-object argument did not return " + "error"); + if (!json_object_iter_set_new(num, iter, json_incref(num))) + fail("json_object_iter_set_new with non-object argument did not return " + "error"); + if (!json_object_iter_set_new(obj, NULL, json_incref(num))) + fail("json_object_iter_set_new with NULL iter did not return error"); + if (!json_object_iter_set_new(obj, iter, NULL)) + fail("json_object_iter_set_new with NULL value did not return error"); + + if (obj->refcount != 1) + fail("unexpected reference count for obj"); + + if (num->refcount != 1) + fail("unexpected reference count for num"); + + json_decref(obj); + json_decref(num); +} + +static void run_tests() { + test_misc(); + test_clear(); + test_update(); + test_set_many_keys(); + test_conditional_updates(); + test_recursive_updates(); + test_circular(); + test_set_nocheck(); + test_iterators(); + test_preserve_order(); + test_object_foreach(); + test_object_foreach_safe(); + test_bad_args(); +} diff --git a/lib/jansson/test/suites/api/test_pack.c b/lib/jansson/test/suites/api/test_pack.c new file mode 100644 index 0000000..865f60b --- /dev/null +++ b/lib/jansson/test/suites/api/test_pack.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2010-2012 Graeme Smecher + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "util.h" +#include +#include +#include +#include + +#ifdef INFINITY +// This test triggers "warning C4756: overflow in constant arithmetic" +// in Visual Studio. This warning is triggered here by design, so disable it. +// (This can only be done on function level so we keep these tests separate) +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4756) +#endif +static void test_inifity() { + json_error_t error; + + if (json_pack_ex(&error, 0, "f", INFINITY)) + fail("json_pack infinity incorrectly succeeded"); + check_error(json_error_numeric_overflow, "Invalid floating point value", "", 1, + 1, 1); + + if (json_pack_ex(&error, 0, "[f]", INFINITY)) + fail("json_pack infinity array element incorrectly succeeded"); + check_error(json_error_numeric_overflow, "Invalid floating point value", "", 1, + 2, 2); + + if (json_pack_ex(&error, 0, "{s:f}", "key", INFINITY)) + fail("json_pack infinity object value incorrectly succeeded"); + check_error(json_error_numeric_overflow, "Invalid floating point value", "", 1, + 4, 4); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} +#endif // INFINITY + +static void run_tests() { + json_t *value; + int i; + char buffer[4] = {'t', 'e', 's', 't'}; + json_error_t error; + + /* + * Simple, valid json_pack cases + */ + /* true */ + value = json_pack("b", 1); + if (!json_is_true(value)) + fail("json_pack boolean failed"); + if (value->refcount != (size_t)-1) + fail("json_pack boolean refcount failed"); + json_decref(value); + + /* false */ + value = json_pack("b", 0); + if (!json_is_false(value)) + fail("json_pack boolean failed"); + if (value->refcount != (size_t)-1) + fail("json_pack boolean refcount failed"); + json_decref(value); + + /* null */ + value = json_pack("n"); + if (!json_is_null(value)) + fail("json_pack null failed"); + if (value->refcount != (size_t)-1) + fail("json_pack null refcount failed"); + json_decref(value); + + /* integer */ + value = json_pack("i", 1); + if (!json_is_integer(value) || json_integer_value(value) != 1) + fail("json_pack integer failed"); + if (value->refcount != (size_t)1) + fail("json_pack integer refcount failed"); + json_decref(value); + + /* integer from json_int_t */ + value = json_pack("I", (json_int_t)555555); + if (!json_is_integer(value) || json_integer_value(value) != 555555) + fail("json_pack json_int_t failed"); + if (value->refcount != (size_t)1) + fail("json_pack integer refcount failed"); + json_decref(value); + + /* real */ + value = json_pack("f", 1.0); + if (!json_is_real(value) || json_real_value(value) != 1.0) + fail("json_pack real failed"); + if (value->refcount != (size_t)1) + fail("json_pack real refcount failed"); + json_decref(value); + + /* string */ + value = json_pack("s", "test"); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack string failed"); + if (value->refcount != (size_t)1) + fail("json_pack string refcount failed"); + json_decref(value); + + /* nullable string (defined case) */ + value = json_pack("s?", "test"); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack nullable string (defined case) failed"); + if (value->refcount != (size_t)1) + fail("json_pack nullable string (defined case) refcount failed"); + json_decref(value); + + /* nullable string (NULL case) */ + value = json_pack("s?", NULL); + if (!json_is_null(value)) + fail("json_pack nullable string (NULL case) failed"); + if (value->refcount != (size_t)-1) + fail("json_pack nullable string (NULL case) refcount failed"); + json_decref(value); + + /* nullable string concatenation */ + if (json_pack_ex(&error, 0, "s?+", "test", "ing")) + fail("json_pack failed to catch invalid format 's?+'"); + check_error(json_error_invalid_format, "Cannot use '+' on optional strings", + "", 1, 2, 2); + + /* nullable string with integer length */ + if (json_pack_ex(&error, 0, "s?#", "test", 4)) + fail("json_pack failed to catch invalid format 's?#'"); + check_error(json_error_invalid_format, "Cannot use '#' on optional strings", + "", 1, 2, 2); + + /* string and length (int) */ + value = json_pack("s#", "test asdf", 4); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack string and length failed"); + if (value->refcount != (size_t)1) + fail("json_pack string and length refcount failed"); + json_decref(value); + + /* string and length (size_t) */ + value = json_pack("s%", "test asdf", (size_t)4); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack string and length failed"); + if (value->refcount != (size_t)1) + fail("json_pack string and length refcount failed"); + json_decref(value); + + /* string and length (int), non-NUL terminated string */ + value = json_pack("s#", buffer, 4); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack string and length (int) failed"); + if (value->refcount != (size_t)1) + fail("json_pack string and length (int) refcount failed"); + json_decref(value); + + /* string and length (size_t), non-NUL terminated string */ + value = json_pack("s%", buffer, (size_t)4); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack string and length (size_t) failed"); + if (value->refcount != (size_t)1) + fail("json_pack string and length (size_t) refcount failed"); + json_decref(value); + + /* string concatenation */ + if (json_pack("s+", "test", NULL)) + fail("json_pack string concatenation succeeded with NULL string"); + + value = json_pack("s++", "te", "st", "ing"); + if (!json_is_string(value) || strcmp("testing", json_string_value(value))) + fail("json_pack string concatenation failed"); + if (value->refcount != (size_t)1) + fail("json_pack string concatenation refcount failed"); + json_decref(value); + + /* string concatenation and length (int) */ + value = json_pack("s#+#+", "test", 1, "test", 2, "test"); + if (!json_is_string(value) || strcmp("ttetest", json_string_value(value))) + fail("json_pack string concatenation and length (int) failed"); + if (value->refcount != (size_t)1) + fail("json_pack string concatenation and length (int) refcount failed"); + json_decref(value); + + /* string concatenation and length (size_t) */ + value = json_pack("s%+%+", "test", (size_t)1, "test", (size_t)2, "test"); + if (!json_is_string(value) || strcmp("ttetest", json_string_value(value))) + fail("json_pack string concatenation and length (size_t) failed"); + if (value->refcount != (size_t)1) + fail("json_pack string concatenation and length (size_t) refcount " + "failed"); + json_decref(value); + + /* empty object */ + value = json_pack("{}", 1.0); + if (!json_is_object(value) || json_object_size(value) != 0) + fail("json_pack empty object failed"); + if (value->refcount != (size_t)1) + fail("json_pack empty object refcount failed"); + json_decref(value); + + /* empty list */ + value = json_pack("[]", 1.0); + if (!json_is_array(value) || json_array_size(value) != 0) + fail("json_pack empty list failed"); + if (value->refcount != (size_t)1) + fail("json_pack empty list failed"); + json_decref(value); + + /* non-incref'd object */ + value = json_pack("o", json_integer(1)); + if (!json_is_integer(value) || json_integer_value(value) != 1) + fail("json_pack object failed"); + if (value->refcount != (size_t)1) + fail("json_pack integer refcount failed"); + json_decref(value); + + /* non-incref'd nullable object (defined case) */ + value = json_pack("o?", json_integer(1)); + if (!json_is_integer(value) || json_integer_value(value) != 1) + fail("json_pack nullable object (defined case) failed"); + if (value->refcount != (size_t)1) + fail("json_pack nullable object (defined case) refcount failed"); + json_decref(value); + + /* non-incref'd nullable object (NULL case) */ + value = json_pack("o?", NULL); + if (!json_is_null(value)) + fail("json_pack nullable object (NULL case) failed"); + if (value->refcount != (size_t)-1) + fail("json_pack nullable object (NULL case) refcount failed"); + json_decref(value); + + /* incref'd object */ + value = json_pack("O", json_integer(1)); + if (!json_is_integer(value) || json_integer_value(value) != 1) + fail("json_pack object failed"); + if (value->refcount != (size_t)2) + fail("json_pack integer refcount failed"); + json_decref(value); + json_decref(value); + + /* incref'd nullable object (defined case) */ + value = json_pack("O?", json_integer(1)); + if (!json_is_integer(value) || json_integer_value(value) != 1) + fail("json_pack incref'd nullable object (defined case) failed"); + if (value->refcount != (size_t)2) + fail("json_pack incref'd nullable object (defined case) refcount " + "failed"); + json_decref(value); + json_decref(value); + + /* incref'd nullable object (NULL case) */ + value = json_pack("O?", NULL); + if (!json_is_null(value)) + fail("json_pack incref'd nullable object (NULL case) failed"); + if (value->refcount != (size_t)-1) + fail("json_pack incref'd nullable object (NULL case) refcount failed"); + + /* simple object */ + value = json_pack("{s:[]}", "foo"); + if (!json_is_object(value) || json_object_size(value) != 1) + fail("json_pack array failed"); + if (!json_is_array(json_object_get(value, "foo"))) + fail("json_pack array failed"); + if (json_object_get(value, "foo")->refcount != (size_t)1) + fail("json_pack object refcount failed"); + json_decref(value); + + /* object with complex key */ + value = json_pack("{s+#+: []}", "foo", "barbar", 3, "baz"); + if (!json_is_object(value) || json_object_size(value) != 1) + fail("json_pack array failed"); + if (!json_is_array(json_object_get(value, "foobarbaz"))) + fail("json_pack array failed"); + if (json_object_get(value, "foobarbaz")->refcount != (size_t)1) + fail("json_pack object refcount failed"); + json_decref(value); + + /* object with optional members */ + value = json_pack("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL); + if (value) + fail("json_pack object optional incorrectly succeeded"); + + value = json_pack("{s:**}", "a", NULL); + if (value) + fail("json_pack object optional invalid incorrectly succeeded"); + + if (json_pack_ex(&error, 0, "{s:i*}", "a", 1)) + fail("json_pack object optional invalid incorrectly succeeded"); + check_error(json_error_invalid_format, "Expected format 's', got '*'", "", 1, + 5, 5); + + value = json_pack("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL); + if (!json_is_object(value) || json_object_size(value) != 0) + fail("json_pack object optional failed"); + json_decref(value); + + value = json_pack("{s:s*}", "key", "\xff\xff"); + if (value) + fail("json_pack object optional with invalid UTF-8 incorrectly " + "succeeded"); + + if (json_pack_ex(&error, 0, "{s: s*#}", "key", "test", 1)) + fail("json_pack failed to catch invalid format 's*#'"); + check_error(json_error_invalid_format, "Cannot use '#' on optional strings", + "", 1, 6, 6); + + if (json_pack_ex(&error, 0, "{s: s*+}", "key", "test", "ing")) + fail("json_pack failed to catch invalid format 's*+'"); + check_error(json_error_invalid_format, "Cannot use '+' on optional strings", + "", 1, 6, 6); + + /* simple array */ + value = json_pack("[i,i,i]", 0, 1, 2); + if (!json_is_array(value) || json_array_size(value) != 3) + fail("json_pack object failed"); + for (i = 0; i < 3; i++) { + if (!json_is_integer(json_array_get(value, i)) || + json_integer_value(json_array_get(value, i)) != i) + + fail("json_pack integer array failed"); + } + json_decref(value); + + /* simple array with optional members */ + value = json_pack("[s,o,O]", NULL, NULL, NULL); + if (value) + fail("json_pack array optional incorrectly succeeded"); + + if (json_pack_ex(&error, 0, "[i*]", 1)) + fail("json_pack array optional invalid incorrectly succeeded"); + check_error(json_error_invalid_format, "Unexpected format character '*'", "", + 1, 3, 3); + + value = json_pack("[**]", NULL); + if (value) + fail("json_pack array optional invalid incorrectly succeeded"); + value = json_pack("[s*,o*,O*]", NULL, NULL, NULL); + if (!json_is_array(value) || json_array_size(value) != 0) + fail("json_pack array optional failed"); + json_decref(value); + +#ifdef NAN + /* Invalid float values */ + if (json_pack_ex(&error, 0, "f", NAN)) + fail("json_pack NAN incorrectly succeeded"); + check_error(json_error_numeric_overflow, "Invalid floating point value", "", 1, + 1, 1); + + if (json_pack_ex(&error, 0, "[f]", NAN)) + fail("json_pack NAN array element incorrectly succeeded"); + check_error(json_error_numeric_overflow, "Invalid floating point value", "", 1, + 2, 2); + + if (json_pack_ex(&error, 0, "{s:f}", "key", NAN)) + fail("json_pack NAN object value incorrectly succeeded"); + check_error(json_error_numeric_overflow, "Invalid floating point value", "", 1, + 4, 4); +#endif + +#ifdef INFINITY + test_inifity(); +#endif + + /* Whitespace; regular string */ + value = json_pack(" s\t ", "test"); + if (!json_is_string(value) || strcmp("test", json_string_value(value))) + fail("json_pack string (with whitespace) failed"); + json_decref(value); + + /* Whitespace; empty array */ + value = json_pack("[ ]"); + if (!json_is_array(value) || json_array_size(value) != 0) + fail("json_pack empty array (with whitespace) failed"); + json_decref(value); + + /* Whitespace; array */ + value = json_pack("[ i , i, i ] ", 1, 2, 3); + if (!json_is_array(value) || json_array_size(value) != 3) + fail("json_pack array (with whitespace) failed"); + json_decref(value); + + /* + * Invalid cases + */ + + /* newline in format string */ + if (json_pack_ex(&error, 0, "{\n\n1")) + fail("json_pack failed to catch invalid format '1'"); + check_error(json_error_invalid_format, "Expected format 's', got '1'", "", 3, + 1, 4); + + /* mismatched open/close array/object */ + if (json_pack_ex(&error, 0, "[}")) + fail("json_pack failed to catch mismatched '}'"); + check_error(json_error_invalid_format, "Unexpected format character '}'", "", + 1, 2, 2); + + if (json_pack_ex(&error, 0, "{]")) + fail("json_pack failed to catch mismatched ']'"); + check_error(json_error_invalid_format, "Expected format 's', got ']'", "", 1, + 2, 2); + + /* missing close array */ + if (json_pack_ex(&error, 0, "[")) + fail("json_pack failed to catch missing ']'"); + check_error(json_error_invalid_format, "Unexpected end of format string", "", + 1, 2, 2); + + /* missing close object */ + if (json_pack_ex(&error, 0, "{")) + fail("json_pack failed to catch missing '}'"); + check_error(json_error_invalid_format, "Unexpected end of format string", "", + 1, 2, 2); + + /* garbage after format string */ + if (json_pack_ex(&error, 0, "[i]a", 42)) + fail("json_pack failed to catch garbage after format string"); + check_error(json_error_invalid_format, "Garbage after format string", "", 1, + 4, 4); + + if (json_pack_ex(&error, 0, "ia", 42)) + fail("json_pack failed to catch garbage after format string"); + check_error(json_error_invalid_format, "Garbage after format string", "", 1, + 2, 2); + + /* NULL string */ + if (json_pack_ex(&error, 0, "s", NULL)) + fail("json_pack failed to catch null argument string"); + check_error(json_error_null_value, "NULL string", "", 1, 1, 1); + + /* + on its own */ + if (json_pack_ex(&error, 0, "+", NULL)) + fail("json_pack failed to a lone +"); + check_error(json_error_invalid_format, "Unexpected format character '+'", "", + 1, 1, 1); + + /* Empty format */ + if (json_pack_ex(&error, 0, "")) + fail("json_pack failed to catch empty format string"); + check_error(json_error_invalid_argument, "NULL or empty format string", "", + -1, -1, 0); + + /* NULL format */ + if (json_pack_ex(&error, 0, NULL)) + fail("json_pack failed to catch NULL format string"); + check_error(json_error_invalid_argument, "NULL or empty format string", "", + -1, -1, 0); + + /* NULL key */ + if (json_pack_ex(&error, 0, "{s:i}", NULL, 1)) + fail("json_pack failed to catch NULL key"); + check_error(json_error_null_value, "NULL object key", "", 1, 2, 2); + + /* NULL value followed by object still steals the object's ref */ + value = json_incref(json_object()); + if (json_pack_ex(&error, 0, "{s:s,s:o}", "badnull", NULL, "dontleak", value)) + fail("json_pack failed to catch NULL value"); + check_error(json_error_null_value, "NULL string", "", 1, 4, 4); + if (value->refcount != (size_t)1) + fail("json_pack failed to steal reference after error."); + json_decref(value); + + /* More complicated checks for row/columns */ + if (json_pack_ex(&error, 0, "{ {}: s }", "foo")) + fail("json_pack failed to catch object as key"); + check_error(json_error_invalid_format, "Expected format 's', got '{'", "", 1, + 3, 3); + + /* Complex object */ + if (json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13)) + fail("json_pack failed to catch missing ]"); + check_error(json_error_invalid_format, "Unexpected format character '}'", "", + 1, 19, 19); + + /* Complex array */ + if (json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]")) + fail("json_pack failed to catch extra }"); + check_error(json_error_invalid_format, "Unexpected format character '}'", "", + 1, 21, 21); + + /* Invalid UTF-8 in object key */ + if (json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42)) + fail("json_pack failed to catch invalid UTF-8 in an object key"); + check_error(json_error_invalid_utf8, "Invalid UTF-8 object key", "", 1, 2, 2); + + /* Invalid UTF-8 in a string */ + if (json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff")) + fail("json_pack failed to catch invalid UTF-8 in a string"); + check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "", 1, 4, 4); + + /* Invalid UTF-8 in an optional '?' string */ + if (json_pack_ex(&error, 0, "{s:s?}", "foo", "\xff\xff")) + fail("json_pack failed to catch invalid UTF-8 in an optional '?' " + "string"); + check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "", 1, 5, 5); + + /* Invalid UTF-8 in an optional '*' string */ + if (json_pack_ex(&error, 0, "{s:s*}", "foo", "\xff\xff")) + fail("json_pack failed to catch invalid UTF-8 in an optional '*' " + "string"); + check_error(json_error_invalid_utf8, "Invalid UTF-8 string", "", 1, 5, 5); + + /* Invalid UTF-8 in a concatenated key */ + if (json_pack_ex(&error, 0, "{s+:i}", "\xff\xff", "concat", 42)) + fail("json_pack failed to catch invalid UTF-8 in an object key"); + check_error(json_error_invalid_utf8, "Invalid UTF-8 object key", "", 1, 3, 3); + + if (json_pack_ex(&error, 0, "{s:o}", "foo", NULL)) + fail("json_pack failed to catch nullable object"); + check_error(json_error_null_value, "NULL object", "", 1, 4, 4); + + if (json_pack_ex(&error, 0, "{s:O}", "foo", NULL)) + fail("json_pack failed to catch nullable incref object"); + check_error(json_error_null_value, "NULL object", "", 1, 4, 4); + + if (json_pack_ex(&error, 0, "{s+:o}", "foo", "bar", NULL)) + fail("json_pack failed to catch non-nullable object value"); + check_error(json_error_null_value, "NULL object", "", 1, 5, 5); + + if (json_pack_ex(&error, 0, "[1s", "Hi")) + fail("json_pack failed to catch invalid format"); + check_error(json_error_invalid_format, "Unexpected format character '1'", "", + 1, 2, 2); + + if (json_pack_ex(&error, 0, "[1s+", "Hi", "ya")) + fail("json_pack failed to catch invalid format"); + check_error(json_error_invalid_format, "Unexpected format character '1'", "", + 1, 2, 2); + + if (json_pack_ex(&error, 0, "[so]", NULL, json_object())) + fail("json_pack failed to catch NULL value"); + check_error(json_error_null_value, "NULL string", "", 1, 2, 2); +} diff --git a/lib/jansson/test/suites/api/test_simple.c b/lib/jansson/test/suites/api/test_simple.c new file mode 100644 index 0000000..7a3f6b7 --- /dev/null +++ b/lib/jansson/test/suites/api/test_simple.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void test_bad_args(void) { + json_t *num = json_integer(1); + json_t *txt = json_string("test"); + + if (!num || !txt) + fail("failed to allocate test objects"); + + if (json_string_nocheck(NULL) != NULL) + fail("json_string_nocheck with NULL argument did not return NULL"); + if (json_stringn_nocheck(NULL, 0) != NULL) + fail("json_stringn_nocheck with NULL argument did not return NULL"); + if (json_string(NULL) != NULL) + fail("json_string with NULL argument did not return NULL"); + if (json_stringn(NULL, 0) != NULL) + fail("json_stringn with NULL argument did not return NULL"); + + if (json_string_length(NULL) != 0) + fail("json_string_length with non-string argument did not return 0"); + if (json_string_length(num) != 0) + fail("json_string_length with non-string argument did not return 0"); + + if (json_string_value(NULL) != NULL) + fail("json_string_value with non-string argument did not return NULL"); + if (json_string_value(num) != NULL) + fail("json_string_value with non-string argument did not return NULL"); + + if (!json_string_setn_nocheck(NULL, "", 0)) + fail("json_string_setn with non-string argument did not return error"); + if (!json_string_setn_nocheck(num, "", 0)) + fail("json_string_setn with non-string argument did not return error"); + if (!json_string_setn_nocheck(txt, NULL, 0)) + fail("json_string_setn_nocheck with NULL value did not return error"); + + if (!json_string_set_nocheck(txt, NULL)) + fail("json_string_set_nocheck with NULL value did not return error"); + if (!json_string_set(txt, NULL)) + fail("json_string_set with NULL value did not return error"); + if (!json_string_setn(txt, NULL, 0)) + fail("json_string_setn with NULL value did not return error"); + + if (num->refcount != 1) + fail("unexpected reference count for num"); + if (txt->refcount != 1) + fail("unexpected reference count for txt"); + + json_decref(num); + json_decref(txt); +} + +/* Call the simple functions not covered by other tests of the public API */ +static void run_tests() { + json_t *value; + + value = json_boolean(1); + if (!json_is_true(value)) + fail("json_boolean(1) failed"); + json_decref(value); + + value = json_boolean(-123); + if (!json_is_true(value)) + fail("json_boolean(-123) failed"); + json_decref(value); + + value = json_boolean(0); + if (!json_is_false(value)) + fail("json_boolean(0) failed"); + if (json_boolean_value(value) != 0) + fail("json_boolean_value failed"); + json_decref(value); + + value = json_integer(1); + if (json_typeof(value) != JSON_INTEGER) + fail("json_typeof failed"); + + if (json_is_object(value)) + fail("json_is_object failed"); + + if (json_is_array(value)) + fail("json_is_array failed"); + + if (json_is_string(value)) + fail("json_is_string failed"); + + if (!json_is_integer(value)) + fail("json_is_integer failed"); + + if (json_is_real(value)) + fail("json_is_real failed"); + + if (!json_is_number(value)) + fail("json_is_number failed"); + + if (json_is_true(value)) + fail("json_is_true failed"); + + if (json_is_false(value)) + fail("json_is_false failed"); + + if (json_is_boolean(value)) + fail("json_is_boolean failed"); + + if (json_is_null(value)) + fail("json_is_null failed"); + + json_decref(value); + + value = json_string("foo"); + if (!value) + fail("json_string failed"); + if (strcmp(json_string_value(value), "foo")) + fail("invalid string value"); + if (json_string_length(value) != 3) + fail("invalid string length"); + + if (json_string_set(value, "barr")) + fail("json_string_set failed"); + if (strcmp(json_string_value(value), "barr")) + fail("invalid string value"); + if (json_string_length(value) != 4) + fail("invalid string length"); + + if (json_string_setn(value, "hi\0ho", 5)) + fail("json_string_set failed"); + if (memcmp(json_string_value(value), "hi\0ho\0", 6)) + fail("invalid string value"); + if (json_string_length(value) != 5) + fail("invalid string length"); + + json_decref(value); + + value = json_string(NULL); + if (value) + fail("json_string(NULL) failed"); + + /* invalid UTF-8 */ + value = json_string("a\xefz"); + if (value) + fail("json_string() failed"); + + value = json_string_nocheck("foo"); + if (!value) + fail("json_string_nocheck failed"); + if (strcmp(json_string_value(value), "foo")) + fail("invalid string value"); + if (json_string_length(value) != 3) + fail("invalid string length"); + + if (json_string_set_nocheck(value, "barr")) + fail("json_string_set_nocheck failed"); + if (strcmp(json_string_value(value), "barr")) + fail("invalid string value"); + if (json_string_length(value) != 4) + fail("invalid string length"); + + if (json_string_setn_nocheck(value, "hi\0ho", 5)) + fail("json_string_set failed"); + if (memcmp(json_string_value(value), "hi\0ho\0", 6)) + fail("invalid string value"); + if (json_string_length(value) != 5) + fail("invalid string length"); + + json_decref(value); + + /* invalid UTF-8 */ + value = json_string_nocheck("qu\xff"); + if (!value) + fail("json_string_nocheck failed"); + if (strcmp(json_string_value(value), "qu\xff")) + fail("invalid string value"); + if (json_string_length(value) != 3) + fail("invalid string length"); + + if (json_string_set_nocheck(value, "\xfd\xfe\xff")) + fail("json_string_set_nocheck failed"); + if (strcmp(json_string_value(value), "\xfd\xfe\xff")) + fail("invalid string value"); + if (json_string_length(value) != 3) + fail("invalid string length"); + + json_decref(value); + + value = json_integer(123); + if (!value) + fail("json_integer failed"); + if (json_integer_value(value) != 123) + fail("invalid integer value"); + if (json_number_value(value) != 123.0) + fail("invalid number value"); + + if (json_integer_set(value, 321)) + fail("json_integer_set failed"); + if (json_integer_value(value) != 321) + fail("invalid integer value"); + if (json_number_value(value) != 321.0) + fail("invalid number value"); + + json_decref(value); + + value = json_real(123.123); + if (!value) + fail("json_real failed"); + if (json_real_value(value) != 123.123) + fail("invalid integer value"); + if (json_number_value(value) != 123.123) + fail("invalid number value"); + + if (json_real_set(value, 321.321)) + fail("json_real_set failed"); + if (json_real_value(value) != 321.321) + fail("invalid real value"); + if (json_number_value(value) != 321.321) + fail("invalid number value"); + + json_decref(value); + + value = json_true(); + if (!value) + fail("json_true failed"); + json_decref(value); + + value = json_false(); + if (!value) + fail("json_false failed"); + json_decref(value); + + value = json_null(); + if (!value) + fail("json_null failed"); + json_decref(value); + + /* Test reference counting on singletons (true, false, null) */ + value = json_true(); + if (value->refcount != (size_t)-1) + fail("refcounting true works incorrectly"); + json_decref(value); + if (value->refcount != (size_t)-1) + fail("refcounting true works incorrectly"); + json_incref(value); + if (value->refcount != (size_t)-1) + fail("refcounting true works incorrectly"); + + value = json_false(); + if (value->refcount != (size_t)-1) + fail("refcounting false works incorrectly"); + json_decref(value); + if (value->refcount != (size_t)-1) + fail("refcounting false works incorrectly"); + json_incref(value); + if (value->refcount != (size_t)-1) + fail("refcounting false works incorrectly"); + + value = json_null(); + if (value->refcount != (size_t)-1) + fail("refcounting null works incorrectly"); + json_decref(value); + if (value->refcount != (size_t)-1) + fail("refcounting null works incorrectly"); + json_incref(value); + if (value->refcount != (size_t)-1) + fail("refcounting null works incorrectly"); + +#ifdef json_auto_t + value = json_string("foo"); + { + json_auto_t *test = json_incref(value); + /* Use test so GCC doesn't complain it is unused. */ + if (!json_is_string(test)) + fail("value type check failed"); + } + if (value->refcount != 1) + fail("automatic decrement failed"); + json_decref(value); +#endif + + test_bad_args(); +} diff --git a/lib/jansson/test/suites/api/test_sprintf.c b/lib/jansson/test/suites/api/test_sprintf.c new file mode 100644 index 0000000..60a0b60 --- /dev/null +++ b/lib/jansson/test/suites/api/test_sprintf.c @@ -0,0 +1,29 @@ +#include "util.h" +#include +#include + +static void test_sprintf() { + json_t *s = json_sprintf("foo bar %d", 42); + if (!s) + fail("json_sprintf returned NULL"); + if (!json_is_string(s)) + fail("json_sprintf didn't return a JSON string"); + if (strcmp(json_string_value(s), "foo bar 42")) + fail("json_sprintf generated an unexpected string"); + + json_decref(s); + + s = json_sprintf("%s", ""); + if (!s) + fail("json_sprintf returned NULL"); + if (!json_is_string(s)) + fail("json_sprintf didn't return a JSON string"); + if (json_string_length(s) != 0) + fail("string is not empty"); + json_decref(s); + + if (json_sprintf("%s", "\xff\xff")) + fail("json_sprintf unexpected success with invalid UTF"); +} + +static void run_tests() { test_sprintf(); } diff --git a/lib/jansson/test/suites/api/test_unpack.c b/lib/jansson/test/suites/api/test_unpack.c new file mode 100644 index 0000000..139ec6f --- /dev/null +++ b/lib/jansson/test/suites/api/test_unpack.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2010-2012 Graeme Smecher + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include +#include + +static void run_tests() { + json_t *j, *j2; + int i1, i2, i3; + json_int_t I1; + int rv; + size_t z; + double f; + char *s; + + json_error_t error; + + /* + * Simple, valid json_pack cases + */ + + /* true */ + rv = json_unpack(json_true(), "b", &i1); + if (rv || !i1) + fail("json_unpack boolean failed"); + + /* false */ + rv = json_unpack(json_false(), "b", &i1); + if (rv || i1) + fail("json_unpack boolean failed"); + + /* null */ + if (json_unpack(json_null(), "n")) + fail("json_unpack null failed"); + + /* integer */ + j = json_integer(42); + rv = json_unpack(j, "i", &i1); + if (rv || i1 != 42) + fail("json_unpack integer failed"); + json_decref(j); + + /* json_int_t */ + j = json_integer(5555555); + rv = json_unpack(j, "I", &I1); + if (rv || I1 != 5555555) + fail("json_unpack json_int_t failed"); + json_decref(j); + + /* real */ + j = json_real(1.7); + rv = json_unpack(j, "f", &f); + if (rv || f != 1.7) + fail("json_unpack real failed"); + json_decref(j); + + /* number */ + j = json_integer(12345); + rv = json_unpack(j, "F", &f); + if (rv || f != 12345.0) + fail("json_unpack (real or) integer failed"); + json_decref(j); + + j = json_real(1.7); + rv = json_unpack(j, "F", &f); + if (rv || f != 1.7) + fail("json_unpack real (or integer) failed"); + json_decref(j); + + /* string */ + j = json_string("foo"); + rv = json_unpack(j, "s", &s); + if (rv || strcmp(s, "foo")) + fail("json_unpack string failed"); + json_decref(j); + + /* string with length (size_t) */ + j = json_string("foo"); + rv = json_unpack(j, "s%", &s, &z); + if (rv || strcmp(s, "foo") || z != 3) + fail("json_unpack string with length (size_t) failed"); + json_decref(j); + + /* empty object */ + j = json_object(); + if (json_unpack(j, "{}")) + fail("json_unpack empty object failed"); + json_decref(j); + + /* empty list */ + j = json_array(); + if (json_unpack(j, "[]")) + fail("json_unpack empty list failed"); + json_decref(j); + + /* non-incref'd object */ + j = json_object(); + rv = json_unpack(j, "o", &j2); + if (rv || j2 != j || j->refcount != 1) + fail("json_unpack object failed"); + json_decref(j); + + /* incref'd object */ + j = json_object(); + rv = json_unpack(j, "O", &j2); + if (rv || j2 != j || j->refcount != 2) + fail("json_unpack object failed"); + json_decref(j); + json_decref(j); + + /* simple object */ + j = json_pack("{s:i}", "foo", 42); + rv = json_unpack(j, "{s:i}", "foo", &i1); + if (rv || i1 != 42) + fail("json_unpack simple object failed"); + json_decref(j); + + /* simple array */ + j = json_pack("[iii]", 1, 2, 3); + rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3); + if (rv || i1 != 1 || i2 != 2 || i3 != 3) + fail("json_unpack simple array failed"); + json_decref(j); + + /* object with many items & strict checking */ + j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3); + rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3); + if (rv || i1 != 1 || i2 != 2 || i3 != 3) + fail("json_unpack object with many items failed"); + json_decref(j); + + /* + * Invalid cases + */ + + j = json_integer(42); + if (!json_unpack_ex(j, &error, 0, "z")) + fail("json_unpack succeeded with invalid format character"); + check_error(json_error_invalid_format, "Unexpected format character 'z'", "", + 1, 1, 1); + + if (!json_unpack_ex(NULL, &error, 0, "[i]")) + fail("json_unpack succeeded with NULL root"); + check_error(json_error_null_value, "NULL root value", "", -1, -1, 0); + json_decref(j); + + /* mismatched open/close array/object */ + j = json_pack("[]"); + if (!json_unpack_ex(j, &error, 0, "[}")) + fail("json_unpack failed to catch mismatched ']'"); + check_error(json_error_invalid_format, "Unexpected format character '}'", "", + 1, 2, 2); + json_decref(j); + + j = json_pack("{}"); + if (!json_unpack_ex(j, &error, 0, "{]")) + fail("json_unpack failed to catch mismatched '}'"); + check_error(json_error_invalid_format, "Expected format 's', got ']'", "", 1, + 2, 2); + json_decref(j); + + /* missing close array */ + j = json_pack("[]"); + if (!json_unpack_ex(j, &error, 0, "[")) + fail("json_unpack failed to catch missing ']'"); + check_error(json_error_invalid_format, "Unexpected end of format string", "", + 1, 2, 2); + json_decref(j); + + /* missing close object */ + j = json_pack("{}"); + if (!json_unpack_ex(j, &error, 0, "{")) + fail("json_unpack failed to catch missing '}'"); + check_error(json_error_invalid_format, "Unexpected end of format string", "", + 1, 2, 2); + json_decref(j); + + /* garbage after format string */ + j = json_pack("[i]", 42); + if (!json_unpack_ex(j, &error, 0, "[i]a", &i1)) + fail("json_unpack failed to catch garbage after format string"); + check_error(json_error_invalid_format, "Garbage after format string", "", 1, + 4, 4); + json_decref(j); + + j = json_integer(12345); + if (!json_unpack_ex(j, &error, 0, "ia", &i1)) + fail("json_unpack failed to catch garbage after format string"); + check_error(json_error_invalid_format, "Garbage after format string", "", 1, + 2, 2); + json_decref(j); + + /* NULL format string */ + j = json_pack("[]"); + if (!json_unpack_ex(j, &error, 0, NULL)) + fail("json_unpack failed to catch null format string"); + check_error(json_error_invalid_argument, "NULL or empty format string", "", + -1, -1, 0); + json_decref(j); + + /* NULL string pointer */ + j = json_string("foobie"); + if (!json_unpack_ex(j, &error, 0, "s", NULL)) + fail("json_unpack failed to catch null string pointer"); + check_error(json_error_null_value, "NULL string argument", "", 1, 1, 1); + json_decref(j); + + /* invalid types */ + j = json_integer(42); + j2 = json_string("foo"); + if (!json_unpack_ex(j, &error, 0, "s")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected string, got integer", "", 1, + 1, 1); + + if (!json_unpack_ex(j, &error, 0, "n")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected null, got integer", "", 1, 1, + 1); + + if (!json_unpack_ex(j, &error, 0, "b")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected true or false, got integer", + "", 1, 1, 1); + + if (!json_unpack_ex(j2, &error, 0, "i")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected integer, got string", "", 1, + 1, 1); + + if (!json_unpack_ex(j2, &error, 0, "I")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected integer, got string", "", 1, + 1, 1); + + if (!json_unpack_ex(j, &error, 0, "f")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected real, got integer", "", 1, 1, + 1); + + if (!json_unpack_ex(j2, &error, 0, "F")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected real or integer, got string", + "", 1, 1, 1); + + if (!json_unpack_ex(j, &error, 0, "[i]")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected array, got integer", "", 1, + 1, 1); + + if (!json_unpack_ex(j, &error, 0, "{si}", "foo")) + fail("json_unpack failed to catch invalid type"); + check_error(json_error_wrong_type, "Expected object, got integer", "", 1, + 1, 1); + + json_decref(j); + json_decref(j2); + + /* Array index out of range */ + j = json_pack("[i]", 1); + if (!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2)) + fail("json_unpack failed to catch index out of array bounds"); + check_error(json_error_index_out_of_range, "Array index 1 out of range", + "", 1, 3, 3); + json_decref(j); + + /* NULL object key */ + j = json_pack("{si}", "foo", 42); + if (!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1)) + fail("json_unpack failed to catch null string pointer"); + check_error(json_error_null_value, "NULL object key", "", 1, 2, 2); + json_decref(j); + + /* Object key not found */ + j = json_pack("{si}", "foo", 42); + if (!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1)) + fail("json_unpack failed to catch null string pointer"); + check_error(json_error_item_not_found, "Object item not found: baz", "", + 1, 3, 3); + json_decref(j); + + /* + * Strict validation + */ + + j = json_pack("[iii]", 1, 2, 3); + rv = json_unpack(j, "[iii!]", &i1, &i2, &i3); + if (rv || i1 != 1 || i2 != 2 || i3 != 3) + fail("json_unpack array with strict validation failed"); + json_decref(j); + + j = json_pack("[iii]", 1, 2, 3); + if (!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2)) + fail("json_unpack array with strict validation failed"); + check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", + "", 1, 5, 5); + json_decref(j); + + /* Like above, but with JSON_STRICT instead of '!' format */ + j = json_pack("[iii]", 1, 2, 3); + if (!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2)) + fail("json_unpack array with strict validation failed"); + check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", + "", 1, 4, 4); + json_decref(j); + + j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42); + rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1); + if (rv || strcmp(s, "bar") != 0 || i1 != 42) + fail("json_unpack object with strict validation failed"); + json_decref(j); + + /* Unpack the same item twice */ + j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1); + if (!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s)) + fail("json_unpack object with strict validation failed"); + { + const char *possible_errors[] = {"2 object item(s) left unpacked: baz, quux", + "2 object item(s) left unpacked: quux, baz"}; + check_errors(json_error_end_of_input_expected, possible_errors, 2, "", + 1, 10, 10); + } + json_decref(j); + + j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4); + if (json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY, "[i{sisn}[ii]]", "foo", + "bar")) + fail("json_unpack complex value with strict validation failed"); + json_decref(j); + + /* ! and * must be last */ + j = json_pack("[ii]", 1, 2); + if (!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2)) + fail("json_unpack failed to catch ! in the middle of an array"); + check_error(json_error_invalid_format, "Expected ']' after '!', got 'i'", "", + 1, 4, 4); + + if (!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2)) + fail("json_unpack failed to catch * in the middle of an array"); + check_error(json_error_invalid_format, "Expected ']' after '*', got 'i'", "", + 1, 4, 4); + json_decref(j); + + j = json_pack("{sssi}", "foo", "bar", "baz", 42); + if (!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1)) + fail("json_unpack failed to catch ! in the middle of an object"); + check_error(json_error_invalid_format, "Expected '}' after '!', got 's'", "", + 1, 5, 5); + + if (!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1)) + fail("json_unpack failed to catch ! in the middle of an object"); + check_error(json_error_invalid_format, "Expected '}' after '*', got 's'", "", + 1, 5, 5); + json_decref(j); + + /* Error in nested object */ + j = json_pack("{s{snsn}}", "foo", "bar", "baz"); + if (!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar")) + fail("json_unpack nested object with strict validation failed"); + check_error(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz", + "", 1, 7, 7); + json_decref(j); + + /* Error in nested array */ + j = json_pack("[[ii]]", 1, 2); + if (!json_unpack_ex(j, &error, 0, "[[i!]]", &i1)) + fail("json_unpack nested array with strict validation failed"); + check_error(json_error_end_of_input_expected, "1 array item(s) left unpacked", + "", 1, 5, 5); + json_decref(j); + + /* Optional values */ + j = json_object(); + i1 = 0; + if (json_unpack(j, "{s?i}", "foo", &i1)) + fail("json_unpack failed for optional key"); + if (i1 != 0) + fail("json_unpack unpacked an optional key"); + json_decref(j); + + i1 = 0; + j = json_pack("{si}", "foo", 42); + if (json_unpack(j, "{s?i}", "foo", &i1)) + fail("json_unpack failed for an optional value"); + if (i1 != 42) + fail("json_unpack failed to unpack an optional value"); + json_decref(j); + + j = json_object(); + i1 = i2 = i3 = 0; + if (json_unpack(j, "{s?[ii]s?{s{si}}}", "foo", &i1, &i2, "bar", "baz", "quux", &i3)) + fail("json_unpack failed for complex optional values"); + if (i1 != 0 || i2 != 0 || i3 != 0) + fail("json_unpack unexpectedly unpacked something"); + json_decref(j); + + j = json_pack("{s{si}}", "foo", "bar", 42); + if (json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1)) + fail("json_unpack failed for complex optional values"); + if (i1 != 42) + fail("json_unpack failed to unpack"); + json_decref(j); + + /* Combine ? and ! */ + j = json_pack("{si}", "foo", 42); + i1 = i2 = 0; + if (json_unpack(j, "{sis?i!}", "foo", &i1, "bar", &i2)) + fail("json_unpack failed for optional values with strict mode"); + if (i1 != 42) + fail("json_unpack failed to unpack"); + if (i2 != 0) + fail("json_unpack failed to unpack"); + json_decref(j); + + /* But don't compensate a missing key with an optional one. */ + j = json_pack("{sisi}", "foo", 42, "baz", 43); + i1 = i2 = i3 = 0; + if (!json_unpack_ex(j, &error, 0, "{sis?i!}", "foo", &i1, "bar", &i2)) + fail("json_unpack failed for optional values with strict mode and " + "compensation"); + check_error(json_error_end_of_input_expected, "1 object item(s) left unpacked: baz", + "", 1, 8, 8); + json_decref(j); +} diff --git a/lib/jansson/test/suites/api/test_version.c b/lib/jansson/test/suites/api/test_version.c new file mode 100644 index 0000000..05e0e96 --- /dev/null +++ b/lib/jansson/test/suites/api/test_version.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Sean Bright + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "util.h" +#include +#include + +static void test_version_str(void) { + if (strcmp(jansson_version_str(), JANSSON_VERSION)) { + fail("jansson_version_str returned invalid version string"); + } +} + +static void test_version_cmp() { + if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION, + JANSSON_MICRO_VERSION)) { + fail("jansson_version_cmp equality check failed"); + } + + if (jansson_version_cmp(JANSSON_MAJOR_VERSION - 1, 0, 0) <= 0) { + fail("jansson_version_cmp less than check failed"); + } + + if (JANSSON_MINOR_VERSION) { + if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION - 1, + JANSSON_MICRO_VERSION) <= 0) { + fail("jansson_version_cmp less than check failed"); + } + } + + if (JANSSON_MICRO_VERSION) { + if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION, + JANSSON_MICRO_VERSION - 1) <= 0) { + fail("jansson_version_cmp less than check failed"); + } + } + + if (jansson_version_cmp(JANSSON_MAJOR_VERSION + 1, JANSSON_MINOR_VERSION, + JANSSON_MICRO_VERSION) >= 0) { + fail("jansson_version_cmp greater than check failed"); + } + + if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION + 1, + JANSSON_MICRO_VERSION) >= 0) { + fail("jansson_version_cmp greater than check failed"); + } + + if (jansson_version_cmp(JANSSON_MAJOR_VERSION, JANSSON_MINOR_VERSION, + JANSSON_MICRO_VERSION + 1) >= 0) { + fail("jansson_version_cmp greater than check failed"); + } +} + +static void run_tests() { + test_version_str(); + test_version_cmp(); +} diff --git a/lib/jansson/test/suites/api/util.h b/lib/jansson/test/suites/api/util.h new file mode 100644 index 0000000..d964c49 --- /dev/null +++ b/lib/jansson/test/suites/api/util.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UTIL_H +#define UTIL_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#if HAVE_LOCALE_H +#include +#endif + +#include + +#define failhdr fprintf(stderr, "%s:%d: ", __FILE__, __LINE__) + +#define fail(msg) \ + do { \ + failhdr; \ + fprintf(stderr, "%s\n", msg); \ + exit(1); \ + } while (0) + +/* Assumes json_error_t error */ +#define check_errors(code_, texts_, num_, source_, line_, column_, position_) \ + do { \ + int i_, found_ = 0; \ + if (json_error_code(&error) != code_) { \ + failhdr; \ + fprintf(stderr, "code: %d != %d\n", json_error_code(&error), code_); \ + exit(1); \ + } \ + for (i_ = 0; i_ < num_; i_++) { \ + if (strcmp(error.text, texts_[i_]) == 0) { \ + found_ = 1; \ + break; \ + } \ + } \ + if (!found_) { \ + failhdr; \ + if (num_ == 1) { \ + fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, texts_[0]); \ + } else { \ + fprintf(stderr, "text: \"%s\" does not match\n", error.text); \ + } \ + exit(1); \ + } \ + if (strcmp(error.source, source_) != 0) { \ + failhdr; \ + \ + fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \ + exit(1); \ + } \ + if (error.line != line_) { \ + failhdr; \ + fprintf(stderr, "line: %d != %d\n", error.line, line_); \ + exit(1); \ + } \ + if (error.column != column_) { \ + failhdr; \ + fprintf(stderr, "column: %d != %d\n", error.column, column_); \ + exit(1); \ + } \ + if (error.position != position_) { \ + failhdr; \ + fprintf(stderr, "position: %d != %d\n", error.position, position_); \ + exit(1); \ + } \ + } while (0) + +/* Assumes json_error_t error */ +#define check_error(code_, text_, source_, line_, column_, position_) \ + check_errors(code_, &text_, 1, source_, line_, column_, position_) + +static void run_tests(); + +int main() { +#ifdef HAVE_SETLOCALE + setlocale(LC_ALL, ""); +#endif + run_tests(); + return 0; +} + +#endif diff --git a/lib/jansson/test/suites/encoding-flags/array/input b/lib/jansson/test/suites/encoding-flags/array/input new file mode 100644 index 0000000..44e2ace --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/array/input @@ -0,0 +1 @@ +[1, 2] diff --git a/lib/jansson/test/suites/encoding-flags/array/output b/lib/jansson/test/suites/encoding-flags/array/output new file mode 100644 index 0000000..fd8ef09 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/array/output @@ -0,0 +1 @@ +[1, 2] \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/compact-array/env b/lib/jansson/test/suites/encoding-flags/compact-array/env new file mode 100644 index 0000000..beddaa8 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/compact-array/env @@ -0,0 +1 @@ +JSON_COMPACT=1 diff --git a/lib/jansson/test/suites/encoding-flags/compact-array/input b/lib/jansson/test/suites/encoding-flags/compact-array/input new file mode 100644 index 0000000..44e2ace --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/compact-array/input @@ -0,0 +1 @@ +[1, 2] diff --git a/lib/jansson/test/suites/encoding-flags/compact-array/output b/lib/jansson/test/suites/encoding-flags/compact-array/output new file mode 100644 index 0000000..3169929 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/compact-array/output @@ -0,0 +1 @@ +[1,2] \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/compact-object/env b/lib/jansson/test/suites/encoding-flags/compact-object/env new file mode 100644 index 0000000..57cf7b4 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/compact-object/env @@ -0,0 +1,2 @@ +JSON_COMPACT=1 +HASHSEED=1 diff --git a/lib/jansson/test/suites/encoding-flags/compact-object/input b/lib/jansson/test/suites/encoding-flags/compact-object/input new file mode 100644 index 0000000..062e54f --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/compact-object/input @@ -0,0 +1 @@ +{"a": 1, "b": 2} diff --git a/lib/jansson/test/suites/encoding-flags/compact-object/output b/lib/jansson/test/suites/encoding-flags/compact-object/output new file mode 100644 index 0000000..73a5d70 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/compact-object/output @@ -0,0 +1 @@ +{"a":1,"b":2} \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/ensure-ascii/env b/lib/jansson/test/suites/encoding-flags/ensure-ascii/env new file mode 100644 index 0000000..7867c32 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/ensure-ascii/env @@ -0,0 +1 @@ +JSON_ENSURE_ASCII=1 diff --git a/lib/jansson/test/suites/encoding-flags/ensure-ascii/input b/lib/jansson/test/suites/encoding-flags/ensure-ascii/input new file mode 100644 index 0000000..69469ce --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/ensure-ascii/input @@ -0,0 +1,8 @@ +[ + "foo", + "Ã¥ ä ö", + "foo åä", + "åä foo", + "Ã¥ foo ä", + "clef g: ð„ž" +] diff --git a/lib/jansson/test/suites/encoding-flags/ensure-ascii/output b/lib/jansson/test/suites/encoding-flags/ensure-ascii/output new file mode 100644 index 0000000..94fa79d --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/ensure-ascii/output @@ -0,0 +1 @@ +["foo", "\u00E5 \u00E4 \u00F6", "foo \u00E5\u00E4", "\u00E5\u00E4 foo", "\u00E5 foo \u00E4", "clef g: \uD834\uDD1E"] \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/indent-array/env b/lib/jansson/test/suites/encoding-flags/indent-array/env new file mode 100644 index 0000000..11f6684 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-array/env @@ -0,0 +1 @@ +JSON_INDENT=4 diff --git a/lib/jansson/test/suites/encoding-flags/indent-array/input b/lib/jansson/test/suites/encoding-flags/indent-array/input new file mode 100644 index 0000000..44e2ace --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-array/input @@ -0,0 +1 @@ +[1, 2] diff --git a/lib/jansson/test/suites/encoding-flags/indent-array/output b/lib/jansson/test/suites/encoding-flags/indent-array/output new file mode 100644 index 0000000..c57d705 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-array/output @@ -0,0 +1,4 @@ +[ + 1, + 2 +] \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/indent-compact-array/env b/lib/jansson/test/suites/encoding-flags/indent-compact-array/env new file mode 100644 index 0000000..f6a3b3e --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-compact-array/env @@ -0,0 +1,2 @@ +JSON_INDENT=4 +JSON_COMPACT=1 diff --git a/lib/jansson/test/suites/encoding-flags/indent-compact-array/input b/lib/jansson/test/suites/encoding-flags/indent-compact-array/input new file mode 100644 index 0000000..44e2ace --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-compact-array/input @@ -0,0 +1 @@ +[1, 2] diff --git a/lib/jansson/test/suites/encoding-flags/indent-compact-array/output b/lib/jansson/test/suites/encoding-flags/indent-compact-array/output new file mode 100644 index 0000000..c57d705 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-compact-array/output @@ -0,0 +1,4 @@ +[ + 1, + 2 +] \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/indent-compact-object/env b/lib/jansson/test/suites/encoding-flags/indent-compact-object/env new file mode 100644 index 0000000..d3a706e --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-compact-object/env @@ -0,0 +1,3 @@ +JSON_INDENT=4 +JSON_COMPACT=1 +HASHSEED=1 diff --git a/lib/jansson/test/suites/encoding-flags/indent-compact-object/input b/lib/jansson/test/suites/encoding-flags/indent-compact-object/input new file mode 100644 index 0000000..062e54f --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-compact-object/input @@ -0,0 +1 @@ +{"a": 1, "b": 2} diff --git a/lib/jansson/test/suites/encoding-flags/indent-compact-object/output b/lib/jansson/test/suites/encoding-flags/indent-compact-object/output new file mode 100644 index 0000000..9cc4294 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-compact-object/output @@ -0,0 +1,4 @@ +{ + "a":1, + "b":2 +} \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/indent-object/env b/lib/jansson/test/suites/encoding-flags/indent-object/env new file mode 100644 index 0000000..f08e847 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-object/env @@ -0,0 +1,2 @@ +JSON_INDENT=4 +HASHSEED=1 diff --git a/lib/jansson/test/suites/encoding-flags/indent-object/input b/lib/jansson/test/suites/encoding-flags/indent-object/input new file mode 100644 index 0000000..062e54f --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-object/input @@ -0,0 +1 @@ +{"a": 1, "b": 2} diff --git a/lib/jansson/test/suites/encoding-flags/indent-object/output b/lib/jansson/test/suites/encoding-flags/indent-object/output new file mode 100644 index 0000000..0fbddba --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/indent-object/output @@ -0,0 +1,4 @@ +{ + "a": 1, + "b": 2 +} \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/object/env b/lib/jansson/test/suites/encoding-flags/object/env new file mode 100644 index 0000000..9e1ca2a --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/object/env @@ -0,0 +1 @@ +HASHSEED=1 diff --git a/lib/jansson/test/suites/encoding-flags/object/input b/lib/jansson/test/suites/encoding-flags/object/input new file mode 100644 index 0000000..062e54f --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/object/input @@ -0,0 +1 @@ +{"a": 1, "b": 2} diff --git a/lib/jansson/test/suites/encoding-flags/object/output b/lib/jansson/test/suites/encoding-flags/object/output new file mode 100644 index 0000000..ecd219f --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/object/output @@ -0,0 +1 @@ +{"a": 1, "b": 2} \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/preserve-order/env b/lib/jansson/test/suites/encoding-flags/preserve-order/env new file mode 100644 index 0000000..504bf51 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/preserve-order/env @@ -0,0 +1 @@ +JSON_PRESERVE_ORDER=1 diff --git a/lib/jansson/test/suites/encoding-flags/preserve-order/input b/lib/jansson/test/suites/encoding-flags/preserve-order/input new file mode 100644 index 0000000..27bcf18 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/preserve-order/input @@ -0,0 +1 @@ +{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6} diff --git a/lib/jansson/test/suites/encoding-flags/preserve-order/output b/lib/jansson/test/suites/encoding-flags/preserve-order/output new file mode 100644 index 0000000..7a443f6 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/preserve-order/output @@ -0,0 +1 @@ +{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6} \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/real-precision/env b/lib/jansson/test/suites/encoding-flags/real-precision/env new file mode 100644 index 0000000..8ec3a7c --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/real-precision/env @@ -0,0 +1 @@ +JSON_REAL_PRECISION=4 diff --git a/lib/jansson/test/suites/encoding-flags/real-precision/input b/lib/jansson/test/suites/encoding-flags/real-precision/input new file mode 100644 index 0000000..146aeb9 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/real-precision/input @@ -0,0 +1 @@ +[1.23456789, 1.0, 1.0000000000000002, 1.23456e99, 1.23456e-99, 0.0000000000012345] diff --git a/lib/jansson/test/suites/encoding-flags/real-precision/output b/lib/jansson/test/suites/encoding-flags/real-precision/output new file mode 100644 index 0000000..36c8639 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/real-precision/output @@ -0,0 +1 @@ +[1.235, 1.0, 1.0, 1.235e99, 1.235e-99, 1.235e-12] \ No newline at end of file diff --git a/lib/jansson/test/suites/encoding-flags/run b/lib/jansson/test/suites/encoding-flags/run new file mode 100755 index 0000000..a6f2111 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/run @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +is_test() { + test -d $test_path +} + +run_test() { + $json_process $test_path >$test_log/stdout 2>$test_log/stderr || return 1 + valgrind_check $test_log/stderr || return 1 +} + +show_error() { + valgrind_show_error && return + cat $test_log/stderr +} + +. $top_srcdir/test/scripts/run-tests.sh diff --git a/lib/jansson/test/suites/encoding-flags/sort-keys/env b/lib/jansson/test/suites/encoding-flags/sort-keys/env new file mode 100644 index 0000000..1e27ccf --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/sort-keys/env @@ -0,0 +1 @@ +JSON_SORT_KEYS=1 diff --git a/lib/jansson/test/suites/encoding-flags/sort-keys/input b/lib/jansson/test/suites/encoding-flags/sort-keys/input new file mode 100644 index 0000000..66951d6 --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/sort-keys/input @@ -0,0 +1 @@ +{"foo": 1, "bar": 2, "baz": 3, "quux": 4} diff --git a/lib/jansson/test/suites/encoding-flags/sort-keys/output b/lib/jansson/test/suites/encoding-flags/sort-keys/output new file mode 100644 index 0000000..132d9df --- /dev/null +++ b/lib/jansson/test/suites/encoding-flags/sort-keys/output @@ -0,0 +1 @@ +{"bar": 2, "baz": 3, "foo": 1, "quux": 4} \ No newline at end of file diff --git a/lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/error b/lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/error new file mode 100644 index 0000000..762d2c4 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xed near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/input b/lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/input new file mode 100644 index 0000000..515dd93 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/encoded-surrogate-half/input @@ -0,0 +1 @@ +["í¢« <-- encoded surrogate half"] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/error new file mode 100644 index 0000000..b16dc17 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/error @@ -0,0 +1,2 @@ +1 3 3 +unable to decode byte 0xe5 near '"\' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/input new file mode 100644 index 0000000..57c8bee --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-after-backslash/input @@ -0,0 +1 @@ +["\å"] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/error new file mode 100644 index 0000000..be15386 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/error @@ -0,0 +1,2 @@ +1 1 1 +unable to decode byte 0xe5 diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/input new file mode 100644 index 0000000..ebefcd6 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-array/input @@ -0,0 +1 @@ +[å] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error new file mode 100644 index 0000000..01b4476 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error @@ -0,0 +1,2 @@ +1 4 4 +unable to decode byte 0xe5 near '123' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input new file mode 100644 index 0000000..e512f9a --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input @@ -0,0 +1 @@ +[123å] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/error new file mode 100644 index 0000000..c13583d --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/error @@ -0,0 +1,2 @@ +1 4 4 +unable to decode byte 0xe5 near '"\u' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/input new file mode 100644 index 0000000..2b271b8 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-escape/input @@ -0,0 +1 @@ +["\uå"] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/error new file mode 100644 index 0000000..c7b20b7 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/error @@ -0,0 +1,2 @@ +1 4 4 +unable to decode byte 0xe5 near '1e1' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/input new file mode 100644 index 0000000..d8e83c5 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-exponent/input @@ -0,0 +1 @@ +[1e1å] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/error new file mode 100644 index 0000000..33dfc23 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xe5 near 'a' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/input new file mode 100644 index 0000000..ef03851 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-identifier/input @@ -0,0 +1 @@ +[aå] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/error new file mode 100644 index 0000000..8f08970 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xe5 near '0' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/input new file mode 100644 index 0000000..371226e --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-int/input @@ -0,0 +1 @@ +[0å] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error new file mode 100644 index 0000000..b7660e3 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error @@ -0,0 +1,2 @@ +1 3 3 +unable to decode byte 0xe5 near '1e' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input new file mode 100644 index 0000000..17fc29c --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input @@ -0,0 +1 @@ +[1eå] diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/error b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/error new file mode 100644 index 0000000..0b7039a --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xe5 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/input b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/input new file mode 100644 index 0000000..00b79c0 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/invalid-utf-8-in-string/input @@ -0,0 +1 @@ +["å <-- invalid UTF-8"] diff --git a/lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/error b/lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/error new file mode 100644 index 0000000..8e9a511 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/error @@ -0,0 +1,2 @@ +1 0 0 +unable to decode byte 0xe5 diff --git a/lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/input b/lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/input new file mode 100644 index 0000000..eb80796 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/lone-invalid-utf-8/input @@ -0,0 +1 @@ +å diff --git a/lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/error b/lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/error new file mode 100644 index 0000000..86bbad3 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0x81 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/input b/lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/input new file mode 100644 index 0000000..62a26b6 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/lone-utf-8-continuation-byte/input @@ -0,0 +1 @@ +[""] diff --git a/lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/error b/lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/error new file mode 100644 index 0000000..d07ccb3 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xf4 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/input b/lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/input new file mode 100644 index 0000000..1216186 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/not-in-unicode-range/input @@ -0,0 +1 @@ +["ô¿¿¿"] diff --git a/lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/error b/lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/error new file mode 100644 index 0000000..8a05aba --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xe0 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/input b/lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/input new file mode 100644 index 0000000..0bf909f --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/overlong-3-byte-encoding/input @@ -0,0 +1 @@ +["à€¢ <-- overlong encoding"] diff --git a/lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/error b/lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/error new file mode 100644 index 0000000..7e19c5f --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xf0 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/input b/lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/input new file mode 100644 index 0000000..c6b6313 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/overlong-4-byte-encoding/input @@ -0,0 +1 @@ +["ð€€¢ <-- overlong encoding"] diff --git a/lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/error b/lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/error new file mode 100644 index 0000000..1d382ed --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xc1 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/input b/lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/input new file mode 100644 index 0000000..ef6e10a --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/overlong-ascii-encoding/input @@ -0,0 +1 @@ +["Á"] diff --git a/lib/jansson/test/suites/invalid-unicode/restricted-utf-8/error b/lib/jansson/test/suites/invalid-unicode/restricted-utf-8/error new file mode 100644 index 0000000..d018f5f --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/restricted-utf-8/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xfd near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/restricted-utf-8/input b/lib/jansson/test/suites/invalid-unicode/restricted-utf-8/input new file mode 100644 index 0000000..ba60170 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/restricted-utf-8/input @@ -0,0 +1 @@ +["ý"] diff --git a/lib/jansson/test/suites/invalid-unicode/run b/lib/jansson/test/suites/invalid-unicode/run new file mode 100755 index 0000000..5b542a4 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/run @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +is_test() { + test -d $test_path +} + +run_test() { + $json_process $test_path >$test_log/stdout 2>$test_log/stderr || return 1 + valgrind_check $test_log/stderr$s || return 1 +} + +show_error() { + valgrind_show_error && return + cat $test_log/stderr +} + +. $top_srcdir/test/scripts/run-tests.sh diff --git a/lib/jansson/test/suites/invalid-unicode/truncated-utf-8/error b/lib/jansson/test/suites/invalid-unicode/truncated-utf-8/error new file mode 100644 index 0000000..8a05aba --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/truncated-utf-8/error @@ -0,0 +1,2 @@ +1 2 2 +unable to decode byte 0xe0 near '"' diff --git a/lib/jansson/test/suites/invalid-unicode/truncated-utf-8/input b/lib/jansson/test/suites/invalid-unicode/truncated-utf-8/input new file mode 100644 index 0000000..bce9e18 --- /dev/null +++ b/lib/jansson/test/suites/invalid-unicode/truncated-utf-8/input @@ -0,0 +1 @@ +["àÿ <-- truncated UTF-8"] diff --git a/lib/jansson/test/suites/invalid/apostrophe/error b/lib/jansson/test/suites/invalid/apostrophe/error new file mode 100644 index 0000000..79bb2a0 --- /dev/null +++ b/lib/jansson/test/suites/invalid/apostrophe/error @@ -0,0 +1,2 @@ +1 2 2 +invalid token near ''' diff --git a/lib/jansson/test/suites/invalid/apostrophe/input b/lib/jansson/test/suites/invalid/apostrophe/input new file mode 100644 index 0000000..f2dd4d2 --- /dev/null +++ b/lib/jansson/test/suites/invalid/apostrophe/input @@ -0,0 +1 @@ +[' diff --git a/lib/jansson/test/suites/invalid/ascii-unicode-identifier/error b/lib/jansson/test/suites/invalid/ascii-unicode-identifier/error new file mode 100644 index 0000000..a4d8142 --- /dev/null +++ b/lib/jansson/test/suites/invalid/ascii-unicode-identifier/error @@ -0,0 +1,2 @@ +1 1 1 +'[' or '{' expected near 'a' diff --git a/lib/jansson/test/suites/invalid/ascii-unicode-identifier/input b/lib/jansson/test/suites/invalid/ascii-unicode-identifier/input new file mode 100644 index 0000000..c2c0208 --- /dev/null +++ b/lib/jansson/test/suites/invalid/ascii-unicode-identifier/input @@ -0,0 +1 @@ +aÃ¥ diff --git a/lib/jansson/test/suites/invalid/brace-comma/error b/lib/jansson/test/suites/invalid/brace-comma/error new file mode 100644 index 0000000..ce04621 --- /dev/null +++ b/lib/jansson/test/suites/invalid/brace-comma/error @@ -0,0 +1,2 @@ +1 2 2 +string or '}' expected near ',' diff --git a/lib/jansson/test/suites/invalid/brace-comma/input b/lib/jansson/test/suites/invalid/brace-comma/input new file mode 100644 index 0000000..74a6628 --- /dev/null +++ b/lib/jansson/test/suites/invalid/brace-comma/input @@ -0,0 +1 @@ +{, diff --git a/lib/jansson/test/suites/invalid/bracket-comma/error b/lib/jansson/test/suites/invalid/bracket-comma/error new file mode 100644 index 0000000..ce0a912 --- /dev/null +++ b/lib/jansson/test/suites/invalid/bracket-comma/error @@ -0,0 +1,2 @@ +1 2 2 +unexpected token near ',' diff --git a/lib/jansson/test/suites/invalid/bracket-comma/input b/lib/jansson/test/suites/invalid/bracket-comma/input new file mode 100644 index 0000000..5b911f1 --- /dev/null +++ b/lib/jansson/test/suites/invalid/bracket-comma/input @@ -0,0 +1 @@ +[, diff --git a/lib/jansson/test/suites/invalid/bracket-one-comma/error.normal b/lib/jansson/test/suites/invalid/bracket-one-comma/error.normal new file mode 100644 index 0000000..0248b11 --- /dev/null +++ b/lib/jansson/test/suites/invalid/bracket-one-comma/error.normal @@ -0,0 +1,2 @@ +2 0 4 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/bracket-one-comma/error.strip b/lib/jansson/test/suites/invalid/bracket-one-comma/error.strip new file mode 100644 index 0000000..f89b38f --- /dev/null +++ b/lib/jansson/test/suites/invalid/bracket-one-comma/error.strip @@ -0,0 +1,2 @@ +1 3 3 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/bracket-one-comma/input b/lib/jansson/test/suites/invalid/bracket-one-comma/input new file mode 100644 index 0000000..874691b --- /dev/null +++ b/lib/jansson/test/suites/invalid/bracket-one-comma/input @@ -0,0 +1 @@ +[1, diff --git a/lib/jansson/test/suites/invalid/empty/error b/lib/jansson/test/suites/invalid/empty/error new file mode 100644 index 0000000..f45da6f --- /dev/null +++ b/lib/jansson/test/suites/invalid/empty/error @@ -0,0 +1,2 @@ +1 0 0 +'[' or '{' expected near end of file diff --git a/lib/jansson/test/suites/invalid/empty/input b/lib/jansson/test/suites/invalid/empty/input new file mode 100644 index 0000000..e69de29 diff --git a/lib/jansson/test/suites/invalid/extra-comma-in-array/error b/lib/jansson/test/suites/invalid/extra-comma-in-array/error new file mode 100644 index 0000000..cae86c2 --- /dev/null +++ b/lib/jansson/test/suites/invalid/extra-comma-in-array/error @@ -0,0 +1,2 @@ +1 4 4 +unexpected token near ']' diff --git a/lib/jansson/test/suites/invalid/extra-comma-in-array/input b/lib/jansson/test/suites/invalid/extra-comma-in-array/input new file mode 100644 index 0000000..e8b1a17 --- /dev/null +++ b/lib/jansson/test/suites/invalid/extra-comma-in-array/input @@ -0,0 +1 @@ +[1,] diff --git a/lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/error b/lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/error new file mode 100644 index 0000000..5baeea4 --- /dev/null +++ b/lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/error @@ -0,0 +1,2 @@ +6 1 17 +unexpected token near ']' diff --git a/lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/input b/lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/input new file mode 100644 index 0000000..bcb2a75 --- /dev/null +++ b/lib/jansson/test/suites/invalid/extra-comma-in-multiline-array/input @@ -0,0 +1,6 @@ +[1, +2, +3, +4, +5, +] diff --git a/lib/jansson/test/suites/invalid/garbage-after-newline/error b/lib/jansson/test/suites/invalid/garbage-after-newline/error new file mode 100644 index 0000000..5d2dec3 --- /dev/null +++ b/lib/jansson/test/suites/invalid/garbage-after-newline/error @@ -0,0 +1,2 @@ +2 3 11 +end of file expected near 'foo' diff --git a/lib/jansson/test/suites/invalid/garbage-after-newline/input b/lib/jansson/test/suites/invalid/garbage-after-newline/input new file mode 100644 index 0000000..3614ac7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/garbage-after-newline/input @@ -0,0 +1,2 @@ +[1,2,3] +foo diff --git a/lib/jansson/test/suites/invalid/garbage-at-the-end/error b/lib/jansson/test/suites/invalid/garbage-at-the-end/error new file mode 100644 index 0000000..cdd8175 --- /dev/null +++ b/lib/jansson/test/suites/invalid/garbage-at-the-end/error @@ -0,0 +1,2 @@ +1 10 10 +end of file expected near 'foo' diff --git a/lib/jansson/test/suites/invalid/garbage-at-the-end/input b/lib/jansson/test/suites/invalid/garbage-at-the-end/input new file mode 100644 index 0000000..55aee53 --- /dev/null +++ b/lib/jansson/test/suites/invalid/garbage-at-the-end/input @@ -0,0 +1 @@ +[1,2,3]foo diff --git a/lib/jansson/test/suites/invalid/integer-starting-with-zero/error b/lib/jansson/test/suites/invalid/integer-starting-with-zero/error new file mode 100644 index 0000000..64e0536 --- /dev/null +++ b/lib/jansson/test/suites/invalid/integer-starting-with-zero/error @@ -0,0 +1,2 @@ +1 2 2 +invalid token near '0' diff --git a/lib/jansson/test/suites/invalid/integer-starting-with-zero/input b/lib/jansson/test/suites/invalid/integer-starting-with-zero/input new file mode 100644 index 0000000..12f67e2 --- /dev/null +++ b/lib/jansson/test/suites/invalid/integer-starting-with-zero/input @@ -0,0 +1 @@ +[012] diff --git a/lib/jansson/test/suites/invalid/invalid-escape/error b/lib/jansson/test/suites/invalid/invalid-escape/error new file mode 100644 index 0000000..d9863f7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-escape/error @@ -0,0 +1,2 @@ +1 4 4 +invalid escape near '"\a' diff --git a/lib/jansson/test/suites/invalid/invalid-escape/input b/lib/jansson/test/suites/invalid/invalid-escape/input new file mode 100644 index 0000000..64c7b70 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-escape/input @@ -0,0 +1 @@ +["\a <-- invalid escape"] diff --git a/lib/jansson/test/suites/invalid/invalid-identifier/error b/lib/jansson/test/suites/invalid/invalid-identifier/error new file mode 100644 index 0000000..496c6ab --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-identifier/error @@ -0,0 +1,2 @@ +1 5 5 +invalid token near 'troo' diff --git a/lib/jansson/test/suites/invalid/invalid-identifier/input b/lib/jansson/test/suites/invalid/invalid-identifier/input new file mode 100644 index 0000000..3d2860d --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-identifier/input @@ -0,0 +1 @@ +[troo diff --git a/lib/jansson/test/suites/invalid/invalid-negative-integer/error b/lib/jansson/test/suites/invalid/invalid-negative-integer/error new file mode 100644 index 0000000..f2526c5 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-negative-integer/error @@ -0,0 +1,2 @@ +1 8 8 +']' expected near 'foo' diff --git a/lib/jansson/test/suites/invalid/invalid-negative-integer/input b/lib/jansson/test/suites/invalid/invalid-negative-integer/input new file mode 100644 index 0000000..6196980 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-negative-integer/input @@ -0,0 +1 @@ +[-123foo] diff --git a/lib/jansson/test/suites/invalid/invalid-negative-real/error b/lib/jansson/test/suites/invalid/invalid-negative-real/error new file mode 100644 index 0000000..933158a --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-negative-real/error @@ -0,0 +1,2 @@ +1 12 12 +']' expected near 'foo' diff --git a/lib/jansson/test/suites/invalid/invalid-negative-real/input b/lib/jansson/test/suites/invalid/invalid-negative-real/input new file mode 100644 index 0000000..3c763d3 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-negative-real/input @@ -0,0 +1 @@ +[-123.123foo] diff --git a/lib/jansson/test/suites/invalid/invalid-second-surrogate/error b/lib/jansson/test/suites/invalid/invalid-second-surrogate/error new file mode 100644 index 0000000..e5a2359 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-second-surrogate/error @@ -0,0 +1,2 @@ +1 62 62 +invalid Unicode '\uD888\u3210' diff --git a/lib/jansson/test/suites/invalid/invalid-second-surrogate/input b/lib/jansson/test/suites/invalid/invalid-second-surrogate/input new file mode 100644 index 0000000..b21453f --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-second-surrogate/input @@ -0,0 +1 @@ +["\uD888\u3210 (first surrogate and invalid second surrogate)"] diff --git a/lib/jansson/test/suites/invalid/invalid-unicode-escape/error b/lib/jansson/test/suites/invalid/invalid-unicode-escape/error new file mode 100644 index 0000000..221c762 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-unicode-escape/error @@ -0,0 +1,2 @@ +1 5 5 +invalid escape near '"\uq' diff --git a/lib/jansson/test/suites/invalid/invalid-unicode-escape/input b/lib/jansson/test/suites/invalid/invalid-unicode-escape/input new file mode 100644 index 0000000..f381e85 --- /dev/null +++ b/lib/jansson/test/suites/invalid/invalid-unicode-escape/input @@ -0,0 +1 @@ +["\uqqqq <-- invalid unicode escape"] diff --git a/lib/jansson/test/suites/invalid/lone-open-brace/error.normal b/lib/jansson/test/suites/invalid/lone-open-brace/error.normal new file mode 100644 index 0000000..00dc765 --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-open-brace/error.normal @@ -0,0 +1,2 @@ +2 0 2 +string or '}' expected near end of file diff --git a/lib/jansson/test/suites/invalid/lone-open-brace/error.strip b/lib/jansson/test/suites/invalid/lone-open-brace/error.strip new file mode 100644 index 0000000..bb1c047 --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-open-brace/error.strip @@ -0,0 +1,2 @@ +1 1 1 +string or '}' expected near end of file diff --git a/lib/jansson/test/suites/invalid/lone-open-brace/input b/lib/jansson/test/suites/invalid/lone-open-brace/input new file mode 100644 index 0000000..98232c6 --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-open-brace/input @@ -0,0 +1 @@ +{ diff --git a/lib/jansson/test/suites/invalid/lone-open-bracket/error.normal b/lib/jansson/test/suites/invalid/lone-open-bracket/error.normal new file mode 100644 index 0000000..f463928 --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-open-bracket/error.normal @@ -0,0 +1,2 @@ +2 0 2 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/lone-open-bracket/error.strip b/lib/jansson/test/suites/invalid/lone-open-bracket/error.strip new file mode 100644 index 0000000..2bc07ea --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-open-bracket/error.strip @@ -0,0 +1,2 @@ +1 1 1 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/lone-open-bracket/input b/lib/jansson/test/suites/invalid/lone-open-bracket/input new file mode 100644 index 0000000..558ed37 --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-open-bracket/input @@ -0,0 +1 @@ +[ diff --git a/lib/jansson/test/suites/invalid/lone-second-surrogate/error b/lib/jansson/test/suites/invalid/lone-second-surrogate/error new file mode 100644 index 0000000..bc5f34e --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-second-surrogate/error @@ -0,0 +1,2 @@ +1 40 40 +invalid Unicode '\uDFAA' diff --git a/lib/jansson/test/suites/invalid/lone-second-surrogate/input b/lib/jansson/test/suites/invalid/lone-second-surrogate/input new file mode 100644 index 0000000..328e35c --- /dev/null +++ b/lib/jansson/test/suites/invalid/lone-second-surrogate/input @@ -0,0 +1 @@ +["\uDFAA (second surrogate on it's own)"] diff --git a/lib/jansson/test/suites/invalid/minus-sign-without-number/error b/lib/jansson/test/suites/invalid/minus-sign-without-number/error new file mode 100644 index 0000000..b3a78b9 --- /dev/null +++ b/lib/jansson/test/suites/invalid/minus-sign-without-number/error @@ -0,0 +1,2 @@ +1 2 2 +invalid token near '-' diff --git a/lib/jansson/test/suites/invalid/minus-sign-without-number/input b/lib/jansson/test/suites/invalid/minus-sign-without-number/input new file mode 100644 index 0000000..0337883 --- /dev/null +++ b/lib/jansson/test/suites/invalid/minus-sign-without-number/input @@ -0,0 +1 @@ +[-foo] diff --git a/lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/error b/lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/error new file mode 100644 index 0000000..36adc34 --- /dev/null +++ b/lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/error @@ -0,0 +1,2 @@ +1 3 3 +invalid token near '-0' diff --git a/lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/input b/lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/input new file mode 100644 index 0000000..6fbb7a2 --- /dev/null +++ b/lib/jansson/test/suites/invalid/negative-integer-starting-with-zero/input @@ -0,0 +1 @@ +[-012] diff --git a/lib/jansson/test/suites/invalid/null-byte-in-object-key/error b/lib/jansson/test/suites/invalid/null-byte-in-object-key/error new file mode 100644 index 0000000..3ec685b --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-byte-in-object-key/error @@ -0,0 +1,2 @@ +1 15 15 +NUL byte in object key not supported near '"foo\u0000bar"' diff --git a/lib/jansson/test/suites/invalid/null-byte-in-object-key/input b/lib/jansson/test/suites/invalid/null-byte-in-object-key/input new file mode 100644 index 0000000..593f0f6 --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-byte-in-object-key/input @@ -0,0 +1 @@ +{"foo\u0000bar": 42} \ No newline at end of file diff --git a/lib/jansson/test/suites/invalid/null-byte-in-string/error b/lib/jansson/test/suites/invalid/null-byte-in-string/error new file mode 100644 index 0000000..45f9bd8 --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-byte-in-string/error @@ -0,0 +1,2 @@ +1 12 12 +control character 0x0 near '"null byte ' diff --git a/lib/jansson/test/suites/invalid/null-byte-in-string/input b/lib/jansson/test/suites/invalid/null-byte-in-string/input new file mode 100644 index 0000000000000000000000000000000000000000..268d1f19435baef5ad7a609c65a951f6c8f6b30f GIT binary patch literal 28 jcma!6$}7#uQAnyRNmXD_$jdKLNX*H}FHcQTisb?Thx!RG literal 0 HcmV?d00001 diff --git a/lib/jansson/test/suites/invalid/null-byte-in-string/nostrip b/lib/jansson/test/suites/invalid/null-byte-in-string/nostrip new file mode 100644 index 0000000..80f4bf7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-byte-in-string/nostrip @@ -0,0 +1,2 @@ +The embedded NULL byte breaks json_loads(), which is used instead of +json_loadf() in the stripped tests. diff --git a/lib/jansson/test/suites/invalid/null-byte-outside-string/error b/lib/jansson/test/suites/invalid/null-byte-outside-string/error new file mode 100644 index 0000000..44d4def --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-byte-outside-string/error @@ -0,0 +1,2 @@ +1 2 2 +invalid token near end of file diff --git a/lib/jansson/test/suites/invalid/null-byte-outside-string/input b/lib/jansson/test/suites/invalid/null-byte-outside-string/input new file mode 100644 index 0000000000000000000000000000000000000000..aa550eb0cd16b047736975b6c92ebd989d1981b3 GIT binary patch literal 3 Kcma!M-~s>v9sp(l literal 0 HcmV?d00001 diff --git a/lib/jansson/test/suites/invalid/null-byte-outside-string/nostrip b/lib/jansson/test/suites/invalid/null-byte-outside-string/nostrip new file mode 100644 index 0000000..80f4bf7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-byte-outside-string/nostrip @@ -0,0 +1,2 @@ +The embedded NULL byte breaks json_loads(), which is used instead of +json_loadf() in the stripped tests. diff --git a/lib/jansson/test/suites/invalid/null-escape-in-string/error b/lib/jansson/test/suites/invalid/null-escape-in-string/error new file mode 100644 index 0000000..3f5c8c6 --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-escape-in-string/error @@ -0,0 +1,2 @@ +1 33 33 +\u0000 is not allowed without JSON_ALLOW_NUL diff --git a/lib/jansson/test/suites/invalid/null-escape-in-string/input b/lib/jansson/test/suites/invalid/null-escape-in-string/input new file mode 100644 index 0000000..dbc9c9a --- /dev/null +++ b/lib/jansson/test/suites/invalid/null-escape-in-string/input @@ -0,0 +1 @@ +["null escape \u0000 not allowed"] diff --git a/lib/jansson/test/suites/invalid/null/error b/lib/jansson/test/suites/invalid/null/error new file mode 100644 index 0000000..1f5d464 --- /dev/null +++ b/lib/jansson/test/suites/invalid/null/error @@ -0,0 +1,2 @@ +1 4 4 +'[' or '{' expected near 'null' diff --git a/lib/jansson/test/suites/invalid/null/input b/lib/jansson/test/suites/invalid/null/input new file mode 100644 index 0000000..19765bd --- /dev/null +++ b/lib/jansson/test/suites/invalid/null/input @@ -0,0 +1 @@ +null diff --git a/lib/jansson/test/suites/invalid/object-apostrophes/error b/lib/jansson/test/suites/invalid/object-apostrophes/error new file mode 100644 index 0000000..23fab01 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-apostrophes/error @@ -0,0 +1,2 @@ +1 2 2 +string or '}' expected near ''' diff --git a/lib/jansson/test/suites/invalid/object-apostrophes/input b/lib/jansson/test/suites/invalid/object-apostrophes/input new file mode 100644 index 0000000..52b2905 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-apostrophes/input @@ -0,0 +1 @@ +{'a' diff --git a/lib/jansson/test/suites/invalid/object-garbage-at-end/error b/lib/jansson/test/suites/invalid/object-garbage-at-end/error new file mode 100644 index 0000000..06c4ec1 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-garbage-at-end/error @@ -0,0 +1,2 @@ +1 12 12 +'}' expected near '123' diff --git a/lib/jansson/test/suites/invalid/object-garbage-at-end/input b/lib/jansson/test/suites/invalid/object-garbage-at-end/input new file mode 100644 index 0000000..62c19d7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-garbage-at-end/input @@ -0,0 +1 @@ +{"a":"a" 123} diff --git a/lib/jansson/test/suites/invalid/object-in-unterminated-array/error.normal b/lib/jansson/test/suites/invalid/object-in-unterminated-array/error.normal new file mode 100644 index 0000000..0248b11 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-in-unterminated-array/error.normal @@ -0,0 +1,2 @@ +2 0 4 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/object-in-unterminated-array/error.strip b/lib/jansson/test/suites/invalid/object-in-unterminated-array/error.strip new file mode 100644 index 0000000..f89b38f --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-in-unterminated-array/error.strip @@ -0,0 +1,2 @@ +1 3 3 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/object-in-unterminated-array/input b/lib/jansson/test/suites/invalid/object-in-unterminated-array/input new file mode 100644 index 0000000..ca9ec37 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-in-unterminated-array/input @@ -0,0 +1 @@ +[{} diff --git a/lib/jansson/test/suites/invalid/object-no-colon/error.normal b/lib/jansson/test/suites/invalid/object-no-colon/error.normal new file mode 100644 index 0000000..78d84f7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-no-colon/error.normal @@ -0,0 +1,2 @@ +2 0 5 +':' expected near end of file diff --git a/lib/jansson/test/suites/invalid/object-no-colon/error.strip b/lib/jansson/test/suites/invalid/object-no-colon/error.strip new file mode 100644 index 0000000..528e266 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-no-colon/error.strip @@ -0,0 +1,2 @@ +1 4 4 +':' expected near end of file diff --git a/lib/jansson/test/suites/invalid/object-no-colon/input b/lib/jansson/test/suites/invalid/object-no-colon/input new file mode 100644 index 0000000..107e626 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-no-colon/input @@ -0,0 +1 @@ +{"a" diff --git a/lib/jansson/test/suites/invalid/object-no-value/error.normal b/lib/jansson/test/suites/invalid/object-no-value/error.normal new file mode 100644 index 0000000..47ad902 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-no-value/error.normal @@ -0,0 +1,2 @@ +2 0 6 +unexpected token near end of file diff --git a/lib/jansson/test/suites/invalid/object-no-value/error.strip b/lib/jansson/test/suites/invalid/object-no-value/error.strip new file mode 100644 index 0000000..b36c5e2 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-no-value/error.strip @@ -0,0 +1,2 @@ +1 5 5 +unexpected token near end of file diff --git a/lib/jansson/test/suites/invalid/object-no-value/input b/lib/jansson/test/suites/invalid/object-no-value/input new file mode 100644 index 0000000..f68f262 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-no-value/input @@ -0,0 +1 @@ +{"a": diff --git a/lib/jansson/test/suites/invalid/object-unterminated-value/error.normal b/lib/jansson/test/suites/invalid/object-unterminated-value/error.normal new file mode 100644 index 0000000..2ad76d4 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-unterminated-value/error.normal @@ -0,0 +1,2 @@ +1 7 7 +unexpected newline near '"a' diff --git a/lib/jansson/test/suites/invalid/object-unterminated-value/error.strip b/lib/jansson/test/suites/invalid/object-unterminated-value/error.strip new file mode 100644 index 0000000..385afb5 --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-unterminated-value/error.strip @@ -0,0 +1,2 @@ +1 7 7 +premature end of input near '"a' diff --git a/lib/jansson/test/suites/invalid/object-unterminated-value/input b/lib/jansson/test/suites/invalid/object-unterminated-value/input new file mode 100644 index 0000000..b854d7e --- /dev/null +++ b/lib/jansson/test/suites/invalid/object-unterminated-value/input @@ -0,0 +1 @@ +{"a":"a diff --git a/lib/jansson/test/suites/invalid/real-garbage-after-e/error b/lib/jansson/test/suites/invalid/real-garbage-after-e/error new file mode 100644 index 0000000..b40ffa9 --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-garbage-after-e/error @@ -0,0 +1,2 @@ +1 3 3 +invalid token near '1e' diff --git a/lib/jansson/test/suites/invalid/real-garbage-after-e/input b/lib/jansson/test/suites/invalid/real-garbage-after-e/input new file mode 100644 index 0000000..6a945ac --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-garbage-after-e/input @@ -0,0 +1 @@ +[1ea] diff --git a/lib/jansson/test/suites/invalid/real-negative-overflow/error b/lib/jansson/test/suites/invalid/real-negative-overflow/error new file mode 100644 index 0000000..d7f8e41 --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-negative-overflow/error @@ -0,0 +1,2 @@ +1 15 15 +real number overflow near '-123123e100000' diff --git a/lib/jansson/test/suites/invalid/real-negative-overflow/input b/lib/jansson/test/suites/invalid/real-negative-overflow/input new file mode 100644 index 0000000..b5bd21c --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-negative-overflow/input @@ -0,0 +1 @@ +[-123123e100000] diff --git a/lib/jansson/test/suites/invalid/real-positive-overflow/error b/lib/jansson/test/suites/invalid/real-positive-overflow/error new file mode 100644 index 0000000..55883c9 --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-positive-overflow/error @@ -0,0 +1,2 @@ +1 14 14 +real number overflow near '123123e100000' diff --git a/lib/jansson/test/suites/invalid/real-positive-overflow/input b/lib/jansson/test/suites/invalid/real-positive-overflow/input new file mode 100644 index 0000000..524e53b --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-positive-overflow/input @@ -0,0 +1 @@ +[123123e100000] diff --git a/lib/jansson/test/suites/invalid/real-truncated-at-e/error b/lib/jansson/test/suites/invalid/real-truncated-at-e/error new file mode 100644 index 0000000..b40ffa9 --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-truncated-at-e/error @@ -0,0 +1,2 @@ +1 3 3 +invalid token near '1e' diff --git a/lib/jansson/test/suites/invalid/real-truncated-at-e/input b/lib/jansson/test/suites/invalid/real-truncated-at-e/input new file mode 100644 index 0000000..1d67b7b --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-truncated-at-e/input @@ -0,0 +1 @@ +[1e] diff --git a/lib/jansson/test/suites/invalid/real-truncated-at-point/error b/lib/jansson/test/suites/invalid/real-truncated-at-point/error new file mode 100644 index 0000000..db972e8 --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-truncated-at-point/error @@ -0,0 +1,2 @@ +1 3 3 +invalid token near '1.' diff --git a/lib/jansson/test/suites/invalid/real-truncated-at-point/input b/lib/jansson/test/suites/invalid/real-truncated-at-point/input new file mode 100644 index 0000000..b652b3f --- /dev/null +++ b/lib/jansson/test/suites/invalid/real-truncated-at-point/input @@ -0,0 +1 @@ +[1.] diff --git a/lib/jansson/test/suites/invalid/recursion-depth/error b/lib/jansson/test/suites/invalid/recursion-depth/error new file mode 100644 index 0000000..11e0537 --- /dev/null +++ b/lib/jansson/test/suites/invalid/recursion-depth/error @@ -0,0 +1,2 @@ +1 2049 2049 +maximum parsing depth reached near '[' diff --git a/lib/jansson/test/suites/invalid/recursion-depth/input b/lib/jansson/test/suites/invalid/recursion-depth/input new file mode 100644 index 0000000..bfa47d9 --- /dev/null +++ b/lib/jansson/test/suites/invalid/recursion-depth/input @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ diff --git a/lib/jansson/test/suites/invalid/run b/lib/jansson/test/suites/invalid/run new file mode 100755 index 0000000..ee1ee1a --- /dev/null +++ b/lib/jansson/test/suites/invalid/run @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +is_test() { + test -d $test_path +} + +do_run() { + variant=$1 + s=".$1" + + strip="" + if [ "$variant" = "strip" ]; then + # This test should not be stripped + [ -f $test_path/nostrip ] && return + strip="--strip" + fi + + if ! $json_process $strip $test_path >$test_log/stdout$s 2>$test_log/stderr$s; then + echo $variant >$test_log/variant + return 1 + fi + valgrind_check $test_log/stderr$s || return 1 +} + +run_test() { + do_run normal && do_run strip +} + +show_error() { + valgrind_show_error && return + + read variant < $test_log/variant + s=".$variant" + + echo "VARIANT: $variant" + cat $test_log/stderr$s +} + +. $top_srcdir/test/scripts/run-tests.sh diff --git a/lib/jansson/test/suites/invalid/tab-character-in-string/error b/lib/jansson/test/suites/invalid/tab-character-in-string/error new file mode 100644 index 0000000..9e2f76e --- /dev/null +++ b/lib/jansson/test/suites/invalid/tab-character-in-string/error @@ -0,0 +1,2 @@ +1 2 2 +control character 0x9 near '"' diff --git a/lib/jansson/test/suites/invalid/tab-character-in-string/input b/lib/jansson/test/suites/invalid/tab-character-in-string/input new file mode 100644 index 0000000..3ebae09 --- /dev/null +++ b/lib/jansson/test/suites/invalid/tab-character-in-string/input @@ -0,0 +1 @@ +[" <-- tab character"] diff --git a/lib/jansson/test/suites/invalid/too-big-negative-integer/error b/lib/jansson/test/suites/invalid/too-big-negative-integer/error new file mode 100644 index 0000000..a0640b9 --- /dev/null +++ b/lib/jansson/test/suites/invalid/too-big-negative-integer/error @@ -0,0 +1,2 @@ +1 32 32 +too big negative integer diff --git a/lib/jansson/test/suites/invalid/too-big-negative-integer/input b/lib/jansson/test/suites/invalid/too-big-negative-integer/input new file mode 100644 index 0000000..d6c26f1 --- /dev/null +++ b/lib/jansson/test/suites/invalid/too-big-negative-integer/input @@ -0,0 +1 @@ +[-123123123123123123123123123123] diff --git a/lib/jansson/test/suites/invalid/too-big-positive-integer/error b/lib/jansson/test/suites/invalid/too-big-positive-integer/error new file mode 100644 index 0000000..3bdbefd --- /dev/null +++ b/lib/jansson/test/suites/invalid/too-big-positive-integer/error @@ -0,0 +1,2 @@ +1 31 31 +too big integer diff --git a/lib/jansson/test/suites/invalid/too-big-positive-integer/input b/lib/jansson/test/suites/invalid/too-big-positive-integer/input new file mode 100644 index 0000000..27c8553 --- /dev/null +++ b/lib/jansson/test/suites/invalid/too-big-positive-integer/input @@ -0,0 +1 @@ +[123123123123123123123123123123] diff --git a/lib/jansson/test/suites/invalid/truncated-unicode-surrogate/error b/lib/jansson/test/suites/invalid/truncated-unicode-surrogate/error new file mode 100644 index 0000000..1b99f06 --- /dev/null +++ b/lib/jansson/test/suites/invalid/truncated-unicode-surrogate/error @@ -0,0 +1,2 @@ +1 46 46 +invalid Unicode '\uDADA' diff --git a/lib/jansson/test/suites/invalid/truncated-unicode-surrogate/input b/lib/jansson/test/suites/invalid/truncated-unicode-surrogate/input new file mode 100644 index 0000000..2b340f4 --- /dev/null +++ b/lib/jansson/test/suites/invalid/truncated-unicode-surrogate/input @@ -0,0 +1 @@ +["\uDADA (first surrogate without the second)"] diff --git a/lib/jansson/test/suites/invalid/unicode-identifier/error b/lib/jansson/test/suites/invalid/unicode-identifier/error new file mode 100644 index 0000000..178b0dd --- /dev/null +++ b/lib/jansson/test/suites/invalid/unicode-identifier/error @@ -0,0 +1,2 @@ +1 1 2 +'[' or '{' expected near 'Ã¥' diff --git a/lib/jansson/test/suites/invalid/unicode-identifier/input b/lib/jansson/test/suites/invalid/unicode-identifier/input new file mode 100644 index 0000000..aad321c --- /dev/null +++ b/lib/jansson/test/suites/invalid/unicode-identifier/input @@ -0,0 +1 @@ +Ã¥ diff --git a/lib/jansson/test/suites/invalid/unterminated-array-and-object/error.normal b/lib/jansson/test/suites/invalid/unterminated-array-and-object/error.normal new file mode 100644 index 0000000..5b19804 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-array-and-object/error.normal @@ -0,0 +1,2 @@ +2 0 3 +string or '}' expected near end of file diff --git a/lib/jansson/test/suites/invalid/unterminated-array-and-object/error.strip b/lib/jansson/test/suites/invalid/unterminated-array-and-object/error.strip new file mode 100644 index 0000000..da2bb22 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-array-and-object/error.strip @@ -0,0 +1,2 @@ +1 2 2 +string or '}' expected near end of file diff --git a/lib/jansson/test/suites/invalid/unterminated-array-and-object/input b/lib/jansson/test/suites/invalid/unterminated-array-and-object/input new file mode 100644 index 0000000..cd9dc64 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-array-and-object/input @@ -0,0 +1 @@ +[{ diff --git a/lib/jansson/test/suites/invalid/unterminated-array/error.normal b/lib/jansson/test/suites/invalid/unterminated-array/error.normal new file mode 100644 index 0000000..8025ed1 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-array/error.normal @@ -0,0 +1,2 @@ +2 0 5 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/unterminated-array/error.strip b/lib/jansson/test/suites/invalid/unterminated-array/error.strip new file mode 100644 index 0000000..495d0f7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-array/error.strip @@ -0,0 +1,2 @@ +1 4 4 +']' expected near end of file diff --git a/lib/jansson/test/suites/invalid/unterminated-array/input b/lib/jansson/test/suites/invalid/unterminated-array/input new file mode 100644 index 0000000..727ee81 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-array/input @@ -0,0 +1 @@ +["a" diff --git a/lib/jansson/test/suites/invalid/unterminated-empty-key/error.normal b/lib/jansson/test/suites/invalid/unterminated-empty-key/error.normal new file mode 100644 index 0000000..3d646ab --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-empty-key/error.normal @@ -0,0 +1,2 @@ +1 2 2 +unexpected newline near '"' diff --git a/lib/jansson/test/suites/invalid/unterminated-empty-key/error.strip b/lib/jansson/test/suites/invalid/unterminated-empty-key/error.strip new file mode 100644 index 0000000..94f1947 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-empty-key/error.strip @@ -0,0 +1,2 @@ +1 2 2 +premature end of input near '"' diff --git a/lib/jansson/test/suites/invalid/unterminated-empty-key/input b/lib/jansson/test/suites/invalid/unterminated-empty-key/input new file mode 100644 index 0000000..4117452 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-empty-key/input @@ -0,0 +1 @@ +{" diff --git a/lib/jansson/test/suites/invalid/unterminated-key/error.normal b/lib/jansson/test/suites/invalid/unterminated-key/error.normal new file mode 100644 index 0000000..5f09b77 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-key/error.normal @@ -0,0 +1,2 @@ +1 3 3 +unexpected newline near '"a' diff --git a/lib/jansson/test/suites/invalid/unterminated-key/error.strip b/lib/jansson/test/suites/invalid/unterminated-key/error.strip new file mode 100644 index 0000000..8b6bec4 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-key/error.strip @@ -0,0 +1,2 @@ +1 3 3 +premature end of input near '"a' diff --git a/lib/jansson/test/suites/invalid/unterminated-key/input b/lib/jansson/test/suites/invalid/unterminated-key/input new file mode 100644 index 0000000..705948c --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-key/input @@ -0,0 +1 @@ +{"a diff --git a/lib/jansson/test/suites/invalid/unterminated-object-and-array/error b/lib/jansson/test/suites/invalid/unterminated-object-and-array/error new file mode 100644 index 0000000..ed97be7 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-object-and-array/error @@ -0,0 +1,2 @@ +1 2 2 +string or '}' expected near '[' diff --git a/lib/jansson/test/suites/invalid/unterminated-object-and-array/input b/lib/jansson/test/suites/invalid/unterminated-object-and-array/input new file mode 100644 index 0000000..da35a86 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-object-and-array/input @@ -0,0 +1 @@ +{[ diff --git a/lib/jansson/test/suites/invalid/unterminated-string/error.normal b/lib/jansson/test/suites/invalid/unterminated-string/error.normal new file mode 100644 index 0000000..5f09b77 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-string/error.normal @@ -0,0 +1,2 @@ +1 3 3 +unexpected newline near '"a' diff --git a/lib/jansson/test/suites/invalid/unterminated-string/error.strip b/lib/jansson/test/suites/invalid/unterminated-string/error.strip new file mode 100644 index 0000000..8b6bec4 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-string/error.strip @@ -0,0 +1,2 @@ +1 3 3 +premature end of input near '"a' diff --git a/lib/jansson/test/suites/invalid/unterminated-string/input b/lib/jansson/test/suites/invalid/unterminated-string/input new file mode 100644 index 0000000..38ab6b0 --- /dev/null +++ b/lib/jansson/test/suites/invalid/unterminated-string/input @@ -0,0 +1 @@ +["a diff --git a/lib/jansson/test/suites/valid/complex-array/env b/lib/jansson/test/suites/valid/complex-array/env new file mode 100644 index 0000000..bd89eff --- /dev/null +++ b/lib/jansson/test/suites/valid/complex-array/env @@ -0,0 +1 @@ +JSON_SORT_KEYS=1 \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/complex-array/input b/lib/jansson/test/suites/valid/complex-array/input new file mode 100644 index 0000000..1b9bbb9 --- /dev/null +++ b/lib/jansson/test/suites/valid/complex-array/input @@ -0,0 +1,5 @@ +[1,2,3,4, +"a", "b", "c", +{"foo": "bar", "core": "dump"}, +true, false, true, true, null, false +] diff --git a/lib/jansson/test/suites/valid/complex-array/output b/lib/jansson/test/suites/valid/complex-array/output new file mode 100644 index 0000000..7aefe56 --- /dev/null +++ b/lib/jansson/test/suites/valid/complex-array/output @@ -0,0 +1 @@ +[1, 2, 3, 4, "a", "b", "c", {"core": "dump", "foo": "bar"}, true, false, true, true, null, false] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/empty-array/input b/lib/jansson/test/suites/valid/empty-array/input new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-array/input @@ -0,0 +1 @@ +[] diff --git a/lib/jansson/test/suites/valid/empty-array/output b/lib/jansson/test/suites/valid/empty-array/output new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-array/output @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/empty-object-in-array/input b/lib/jansson/test/suites/valid/empty-object-in-array/input new file mode 100644 index 0000000..93d5140 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-object-in-array/input @@ -0,0 +1 @@ +[{}] diff --git a/lib/jansson/test/suites/valid/empty-object-in-array/output b/lib/jansson/test/suites/valid/empty-object-in-array/output new file mode 100644 index 0000000..ee1aac4 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-object-in-array/output @@ -0,0 +1 @@ +[{}] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/empty-object/input b/lib/jansson/test/suites/valid/empty-object/input new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-object/input @@ -0,0 +1 @@ +{} diff --git a/lib/jansson/test/suites/valid/empty-object/output b/lib/jansson/test/suites/valid/empty-object/output new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-object/output @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/empty-string/input b/lib/jansson/test/suites/valid/empty-string/input new file mode 100644 index 0000000..66a1e18 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-string/input @@ -0,0 +1 @@ +[""] diff --git a/lib/jansson/test/suites/valid/empty-string/output b/lib/jansson/test/suites/valid/empty-string/output new file mode 100644 index 0000000..93b6be2 --- /dev/null +++ b/lib/jansson/test/suites/valid/empty-string/output @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/escaped-utf-control-char/input b/lib/jansson/test/suites/valid/escaped-utf-control-char/input new file mode 100644 index 0000000..9a98545 --- /dev/null +++ b/lib/jansson/test/suites/valid/escaped-utf-control-char/input @@ -0,0 +1 @@ +["\u0012 escaped control character"] diff --git a/lib/jansson/test/suites/valid/escaped-utf-control-char/output b/lib/jansson/test/suites/valid/escaped-utf-control-char/output new file mode 100644 index 0000000..07221b7 --- /dev/null +++ b/lib/jansson/test/suites/valid/escaped-utf-control-char/output @@ -0,0 +1 @@ +["\u0012 escaped control character"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/false/input b/lib/jansson/test/suites/valid/false/input new file mode 100644 index 0000000..4343652 --- /dev/null +++ b/lib/jansson/test/suites/valid/false/input @@ -0,0 +1 @@ +[false] diff --git a/lib/jansson/test/suites/valid/false/output b/lib/jansson/test/suites/valid/false/output new file mode 100644 index 0000000..67b2f07 --- /dev/null +++ b/lib/jansson/test/suites/valid/false/output @@ -0,0 +1 @@ +[false] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/negative-int/input b/lib/jansson/test/suites/valid/negative-int/input new file mode 100644 index 0000000..a96d5cd --- /dev/null +++ b/lib/jansson/test/suites/valid/negative-int/input @@ -0,0 +1 @@ +[-123] diff --git a/lib/jansson/test/suites/valid/negative-int/output b/lib/jansson/test/suites/valid/negative-int/output new file mode 100644 index 0000000..8e30f8b --- /dev/null +++ b/lib/jansson/test/suites/valid/negative-int/output @@ -0,0 +1 @@ +[-123] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/negative-one/input b/lib/jansson/test/suites/valid/negative-one/input new file mode 100644 index 0000000..2363a1a --- /dev/null +++ b/lib/jansson/test/suites/valid/negative-one/input @@ -0,0 +1 @@ +[-1] diff --git a/lib/jansson/test/suites/valid/negative-one/output b/lib/jansson/test/suites/valid/negative-one/output new file mode 100644 index 0000000..99d21a2 --- /dev/null +++ b/lib/jansson/test/suites/valid/negative-one/output @@ -0,0 +1 @@ +[-1] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/negative-zero/input b/lib/jansson/test/suites/valid/negative-zero/input new file mode 100644 index 0000000..40fc49c --- /dev/null +++ b/lib/jansson/test/suites/valid/negative-zero/input @@ -0,0 +1 @@ +[-0] diff --git a/lib/jansson/test/suites/valid/negative-zero/output b/lib/jansson/test/suites/valid/negative-zero/output new file mode 100644 index 0000000..6e7ea63 --- /dev/null +++ b/lib/jansson/test/suites/valid/negative-zero/output @@ -0,0 +1 @@ +[0] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/null/input b/lib/jansson/test/suites/valid/null/input new file mode 100644 index 0000000..62864b3 --- /dev/null +++ b/lib/jansson/test/suites/valid/null/input @@ -0,0 +1 @@ +[null] diff --git a/lib/jansson/test/suites/valid/null/output b/lib/jansson/test/suites/valid/null/output new file mode 100644 index 0000000..500db4a --- /dev/null +++ b/lib/jansson/test/suites/valid/null/output @@ -0,0 +1 @@ +[null] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/one-byte-utf-8/input b/lib/jansson/test/suites/valid/one-byte-utf-8/input new file mode 100644 index 0000000..8bda468 --- /dev/null +++ b/lib/jansson/test/suites/valid/one-byte-utf-8/input @@ -0,0 +1 @@ +["\u002c one-byte UTF-8"] diff --git a/lib/jansson/test/suites/valid/one-byte-utf-8/output b/lib/jansson/test/suites/valid/one-byte-utf-8/output new file mode 100644 index 0000000..c33d250 --- /dev/null +++ b/lib/jansson/test/suites/valid/one-byte-utf-8/output @@ -0,0 +1 @@ +[", one-byte UTF-8"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-capital-e-negative-exponent/input b/lib/jansson/test/suites/valid/real-capital-e-negative-exponent/input new file mode 100644 index 0000000..1e9fa51 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-capital-e-negative-exponent/input @@ -0,0 +1 @@ +[1E-2] diff --git a/lib/jansson/test/suites/valid/real-capital-e-negative-exponent/output b/lib/jansson/test/suites/valid/real-capital-e-negative-exponent/output new file mode 100644 index 0000000..75b9ef9 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-capital-e-negative-exponent/output @@ -0,0 +1 @@ +[0.01] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-capital-e-positive-exponent/input b/lib/jansson/test/suites/valid/real-capital-e-positive-exponent/input new file mode 100644 index 0000000..6a6ab93 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-capital-e-positive-exponent/input @@ -0,0 +1 @@ +[1E+2] diff --git a/lib/jansson/test/suites/valid/real-capital-e-positive-exponent/output b/lib/jansson/test/suites/valid/real-capital-e-positive-exponent/output new file mode 100644 index 0000000..d8ff702 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-capital-e-positive-exponent/output @@ -0,0 +1 @@ +[100.0] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-capital-e/input b/lib/jansson/test/suites/valid/real-capital-e/input new file mode 100644 index 0000000..e703223 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-capital-e/input @@ -0,0 +1 @@ +[1E22] diff --git a/lib/jansson/test/suites/valid/real-capital-e/output b/lib/jansson/test/suites/valid/real-capital-e/output new file mode 100644 index 0000000..9a739f2 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-capital-e/output @@ -0,0 +1 @@ +[1e22] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-exponent-no-dtoa/input b/lib/jansson/test/suites/valid/real-exponent-no-dtoa/input new file mode 100644 index 0000000..c990596 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-exponent-no-dtoa/input @@ -0,0 +1 @@ +[1.23e47, 0.1, 0.3, 9.99] diff --git a/lib/jansson/test/suites/valid/real-exponent-no-dtoa/output b/lib/jansson/test/suites/valid/real-exponent-no-dtoa/output new file mode 100644 index 0000000..c36687c --- /dev/null +++ b/lib/jansson/test/suites/valid/real-exponent-no-dtoa/output @@ -0,0 +1 @@ +[1.2299999999999999e47, 0.10000000000000001, 0.29999999999999999, 9.9900000000000002] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-exponent-no-dtoa/skip_if_dtoa b/lib/jansson/test/suites/valid/real-exponent-no-dtoa/skip_if_dtoa new file mode 100644 index 0000000..e69de29 diff --git a/lib/jansson/test/suites/valid/real-exponent/input b/lib/jansson/test/suites/valid/real-exponent/input new file mode 100644 index 0000000..c990596 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-exponent/input @@ -0,0 +1 @@ +[1.23e47, 0.1, 0.3, 9.99] diff --git a/lib/jansson/test/suites/valid/real-exponent/output b/lib/jansson/test/suites/valid/real-exponent/output new file mode 100644 index 0000000..51c590f --- /dev/null +++ b/lib/jansson/test/suites/valid/real-exponent/output @@ -0,0 +1 @@ +[1.23e47, 0.1, 0.3, 9.99] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-exponent/skip_unless_dtoa b/lib/jansson/test/suites/valid/real-exponent/skip_unless_dtoa new file mode 100644 index 0000000..e69de29 diff --git a/lib/jansson/test/suites/valid/real-fraction-exponent/input b/lib/jansson/test/suites/valid/real-fraction-exponent/input new file mode 100644 index 0000000..0c1660d --- /dev/null +++ b/lib/jansson/test/suites/valid/real-fraction-exponent/input @@ -0,0 +1 @@ +[123.456e78] diff --git a/lib/jansson/test/suites/valid/real-fraction-exponent/output b/lib/jansson/test/suites/valid/real-fraction-exponent/output new file mode 100644 index 0000000..66a3c81 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-fraction-exponent/output @@ -0,0 +1 @@ +[1.23456e80] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-negative-exponent/input b/lib/jansson/test/suites/valid/real-negative-exponent/input new file mode 100644 index 0000000..daa4af9 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-negative-exponent/input @@ -0,0 +1 @@ +[1e-2] diff --git a/lib/jansson/test/suites/valid/real-negative-exponent/output b/lib/jansson/test/suites/valid/real-negative-exponent/output new file mode 100644 index 0000000..75b9ef9 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-negative-exponent/output @@ -0,0 +1 @@ +[0.01] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-positive-exponent/input b/lib/jansson/test/suites/valid/real-positive-exponent/input new file mode 100644 index 0000000..f378077 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-positive-exponent/input @@ -0,0 +1 @@ +[1e+2] diff --git a/lib/jansson/test/suites/valid/real-positive-exponent/output b/lib/jansson/test/suites/valid/real-positive-exponent/output new file mode 100644 index 0000000..d8ff702 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-positive-exponent/output @@ -0,0 +1 @@ +[100.0] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-subnormal-number/input b/lib/jansson/test/suites/valid/real-subnormal-number/input new file mode 100644 index 0000000..df60653 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-subnormal-number/input @@ -0,0 +1 @@ +[1.8011670033376514e-308] diff --git a/lib/jansson/test/suites/valid/real-subnormal-number/output b/lib/jansson/test/suites/valid/real-subnormal-number/output new file mode 100644 index 0000000..e6b0a58 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-subnormal-number/output @@ -0,0 +1 @@ +[1.8011670033376514e-308] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/real-underflow/input b/lib/jansson/test/suites/valid/real-underflow/input new file mode 100644 index 0000000..dc70996 --- /dev/null +++ b/lib/jansson/test/suites/valid/real-underflow/input @@ -0,0 +1 @@ +[123e-10000000] diff --git a/lib/jansson/test/suites/valid/real-underflow/output b/lib/jansson/test/suites/valid/real-underflow/output new file mode 100644 index 0000000..92df1df --- /dev/null +++ b/lib/jansson/test/suites/valid/real-underflow/output @@ -0,0 +1 @@ +[0.0] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/run b/lib/jansson/test/suites/valid/run new file mode 100755 index 0000000..15f89ec --- /dev/null +++ b/lib/jansson/test/suites/valid/run @@ -0,0 +1,51 @@ +#!/bin/sh +# +# Copyright (c) 2009-2016 Petri Lehtinen +# +# Jansson is free software; you can redistribute it and/or modify +# it under the terms of the MIT license. See LICENSE for details. + +dtoa_enabled() { + grep -q "DTOA_ENABLED 1" $top_builddir/jansson_private_config.h +} + +is_test() { + test -d $test_path +} + +do_run() { + if [ -f $test_path/skip_unless_dtoa ]; then + dtoa_enabled || return 77 + fi + if [ -f $test_path/skip_if_dtoa ]; then + dtoa_enabled && return 77 + fi + + variant=$1 + s=".$1" + + strip="" + [ "$variant" = "strip" ] && strip="--strip" + + if ! $json_process $strip $test_path >$test_log/stdout$s 2>$test_log/stderr$s; then + echo $variant >$test_log/variant + return 1 + fi + valgrind_check $test_log/stderr$s || return 1 +} + +run_test() { + do_run normal && do_run strip +} + +show_error() { + valgrind_show_error && return + + read variant < $test_log/variant + s=".$variant" + + echo "VARIANT: $variant" + cat $test_log/stderr$s +} + +. $top_srcdir/test/scripts/run-tests.sh diff --git a/lib/jansson/test/suites/valid/short-string/input b/lib/jansson/test/suites/valid/short-string/input new file mode 100644 index 0000000..0c3426d --- /dev/null +++ b/lib/jansson/test/suites/valid/short-string/input @@ -0,0 +1 @@ +["a"] diff --git a/lib/jansson/test/suites/valid/short-string/output b/lib/jansson/test/suites/valid/short-string/output new file mode 100644 index 0000000..eac5f7b --- /dev/null +++ b/lib/jansson/test/suites/valid/short-string/output @@ -0,0 +1 @@ +["a"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/simple-ascii-string/input b/lib/jansson/test/suites/valid/simple-ascii-string/input new file mode 100644 index 0000000..929b215 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-ascii-string/input @@ -0,0 +1 @@ +["abcdefghijklmnopqrstuvwxyz1234567890 "] diff --git a/lib/jansson/test/suites/valid/simple-ascii-string/output b/lib/jansson/test/suites/valid/simple-ascii-string/output new file mode 100644 index 0000000..90358ab --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-ascii-string/output @@ -0,0 +1 @@ +["abcdefghijklmnopqrstuvwxyz1234567890 "] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/simple-int-0/input b/lib/jansson/test/suites/valid/simple-int-0/input new file mode 100644 index 0000000..111bb86 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-int-0/input @@ -0,0 +1 @@ +[0] diff --git a/lib/jansson/test/suites/valid/simple-int-0/output b/lib/jansson/test/suites/valid/simple-int-0/output new file mode 100644 index 0000000..6e7ea63 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-int-0/output @@ -0,0 +1 @@ +[0] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/simple-int-1/input b/lib/jansson/test/suites/valid/simple-int-1/input new file mode 100644 index 0000000..7660873 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-int-1/input @@ -0,0 +1 @@ +[1] diff --git a/lib/jansson/test/suites/valid/simple-int-1/output b/lib/jansson/test/suites/valid/simple-int-1/output new file mode 100644 index 0000000..bace2a0 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-int-1/output @@ -0,0 +1 @@ +[1] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/simple-int-123/input b/lib/jansson/test/suites/valid/simple-int-123/input new file mode 100644 index 0000000..3214bfe --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-int-123/input @@ -0,0 +1 @@ +[123] diff --git a/lib/jansson/test/suites/valid/simple-int-123/output b/lib/jansson/test/suites/valid/simple-int-123/output new file mode 100644 index 0000000..e47f69a --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-int-123/output @@ -0,0 +1 @@ +[123] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/simple-object/input b/lib/jansson/test/suites/valid/simple-object/input new file mode 100644 index 0000000..a34fb49 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-object/input @@ -0,0 +1 @@ +{"a":[]} diff --git a/lib/jansson/test/suites/valid/simple-object/output b/lib/jansson/test/suites/valid/simple-object/output new file mode 100644 index 0000000..982abe8 --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-object/output @@ -0,0 +1 @@ +{"a": []} \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/simple-real/input b/lib/jansson/test/suites/valid/simple-real/input new file mode 100644 index 0000000..0fed7df --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-real/input @@ -0,0 +1 @@ +[123.456789] diff --git a/lib/jansson/test/suites/valid/simple-real/output b/lib/jansson/test/suites/valid/simple-real/output new file mode 100644 index 0000000..b02878e --- /dev/null +++ b/lib/jansson/test/suites/valid/simple-real/output @@ -0,0 +1 @@ +[123.456789] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/string-escapes/input b/lib/jansson/test/suites/valid/string-escapes/input new file mode 100644 index 0000000..d994564 --- /dev/null +++ b/lib/jansson/test/suites/valid/string-escapes/input @@ -0,0 +1 @@ +["\"\\\/\b\f\n\r\t"] diff --git a/lib/jansson/test/suites/valid/string-escapes/output b/lib/jansson/test/suites/valid/string-escapes/output new file mode 100644 index 0000000..ca5c1c6 --- /dev/null +++ b/lib/jansson/test/suites/valid/string-escapes/output @@ -0,0 +1 @@ +["\"\\/\b\f\n\r\t"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/three-byte-utf-8/input b/lib/jansson/test/suites/valid/three-byte-utf-8/input new file mode 100644 index 0000000..ccc0bfa --- /dev/null +++ b/lib/jansson/test/suites/valid/three-byte-utf-8/input @@ -0,0 +1 @@ +["\u0821 three-byte UTF-8"] diff --git a/lib/jansson/test/suites/valid/three-byte-utf-8/output b/lib/jansson/test/suites/valid/three-byte-utf-8/output new file mode 100644 index 0000000..c44d124 --- /dev/null +++ b/lib/jansson/test/suites/valid/three-byte-utf-8/output @@ -0,0 +1 @@ +["à ¡ three-byte UTF-8"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/true/input b/lib/jansson/test/suites/valid/true/input new file mode 100644 index 0000000..29513c4 --- /dev/null +++ b/lib/jansson/test/suites/valid/true/input @@ -0,0 +1 @@ +[true] diff --git a/lib/jansson/test/suites/valid/true/output b/lib/jansson/test/suites/valid/true/output new file mode 100644 index 0000000..de601e3 --- /dev/null +++ b/lib/jansson/test/suites/valid/true/output @@ -0,0 +1 @@ +[true] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/two-byte-utf-8/input b/lib/jansson/test/suites/valid/two-byte-utf-8/input new file mode 100644 index 0000000..05ae854 --- /dev/null +++ b/lib/jansson/test/suites/valid/two-byte-utf-8/input @@ -0,0 +1 @@ +["\u0123 two-byte UTF-8"] diff --git a/lib/jansson/test/suites/valid/two-byte-utf-8/output b/lib/jansson/test/suites/valid/two-byte-utf-8/output new file mode 100644 index 0000000..1f0988d --- /dev/null +++ b/lib/jansson/test/suites/valid/two-byte-utf-8/output @@ -0,0 +1 @@ +["Ä£ two-byte UTF-8"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/utf-8-string/input b/lib/jansson/test/suites/valid/utf-8-string/input new file mode 100644 index 0000000..20dc64a --- /dev/null +++ b/lib/jansson/test/suites/valid/utf-8-string/input @@ -0,0 +1 @@ +["€þıœəßð some utf-8 ĸʒ×ŋµåäöð„ž"] diff --git a/lib/jansson/test/suites/valid/utf-8-string/output b/lib/jansson/test/suites/valid/utf-8-string/output new file mode 100644 index 0000000..5372865 --- /dev/null +++ b/lib/jansson/test/suites/valid/utf-8-string/output @@ -0,0 +1 @@ +["€þıœəßð some utf-8 ĸʒ×ŋµåäöð„ž"] \ No newline at end of file diff --git a/lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/input b/lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/input new file mode 100644 index 0000000..c598b41 --- /dev/null +++ b/lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/input @@ -0,0 +1 @@ +["\uD834\uDD1E surrogate, four-byte UTF-8"] diff --git a/lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/output b/lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/output new file mode 100644 index 0000000..fa806d2 --- /dev/null +++ b/lib/jansson/test/suites/valid/utf-surrogate-four-byte-encoding/output @@ -0,0 +1 @@ +["ð„ž surrogate, four-byte UTF-8"] \ No newline at end of file diff --git a/lib/janssonpath/.gitignore b/lib/janssonpath/.gitignore new file mode 100644 index 0000000..e2d7c5b --- /dev/null +++ b/lib/janssonpath/.gitignore @@ -0,0 +1,3 @@ +**.o +.vscode +test/test \ No newline at end of file diff --git a/lib/jmespath/LICENSE b/lib/janssonpath/LICENSE similarity index 88% rename from lib/jmespath/LICENSE rename to lib/janssonpath/LICENSE index 8309ee0..b3a70fe 100644 --- a/lib/jmespath/LICENSE +++ b/lib/janssonpath/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2015 Michael Dowling +MIT License + +Copyright (c) 2022 Suika Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7,13 +9,13 @@ 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 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. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/janssonpath/Makefile b/lib/janssonpath/Makefile new file mode 100644 index 0000000..a3c56f4 --- /dev/null +++ b/lib/janssonpath/Makefile @@ -0,0 +1,17 @@ +DBG_FLG = -Wall -Wextra -pedantic + +ifdef debug +DBG_FLG += -g3 -Og -D_DEBUG -fsanitize=address -fno-omit-frame-pointer +LDFLAGS += -lasan +endif + +CFLAGS = -std=c11 $(DBG_FLG) + + +all: janssonpath.o + +janssonpath.o: janssonpath_impl.o parse.o + ld -r $? -o $@ + +clean: + rm *.o \ No newline at end of file diff --git a/lib/janssonpath/README.md b/lib/janssonpath/README.md new file mode 100644 index 0000000..0e3c0da --- /dev/null +++ b/lib/janssonpath/README.md @@ -0,0 +1,74 @@ +# Janssonpath +Jsonpath implementation for [jansson](http://www.digip.org/jansson/)([Github](https://github.com/akheron/jansson)) + +## How to build + +`make` in outmost directory to get janssonpath.o. + +Include janssonpath.h, and link with janssonpath.o and jansson in your project to use the facility. + +Typical command for compile: + +`cc your_sources janssonpath.o -ljansson -o target_name` + +## Usage + +`json_t * json_path_get(json_t *json, const char *path);` + +json_path_get returns the result possiblely with reference to the json and its descendant nodes. + +User is responsible for json_decref-ing the returned json node. + +## Grammar + +Use `"` to enclosing strings. Space characters not allowed except in string literal. + +### Path + +| Expression | Description | +| ------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `$` | The root object or array. In out most path it can be omited. | +| `.property` | Same as `["property"]`. | +| `["property"]` | Selects the specified property in a parent object. When property is integer n, it's alias of [n]. | +| `[n]` | Selects the n-th element from an array. Indexes are 0-based. Note that objects will always return empty lists for this. | +| `..` | Recursive: all descendant node. | +| `*` | Selects all elements in an object or an array, regardless of their names or indexes. | +| `#` | Length of the array. | +| `[start:end]` `[start:]` | Selects array elements from the start index and up to, but not including, end index. If end is omitted, selects all elements from start until the end of the array. Returns a list. | +| `[:n]` | Selects the first n elements of the array. Returns a list. | +| `[-n:]` | Selects the last n elements of the array. Returns a list. | +| `[-n]` | Selects the n-th elements counted from last of the array. Returns a list. | +| `[?(expression)]` | Filter expression. Selects all elements in an object or array that filter expression is true for it. Returns a list. | +| `[(expression)]` | Script expression. Expression is evaluated as property names or indexes to select. | +| `@` | Used in filter expressions to refer to the current node being processed. | + +List are represented in json array. To distinct between list and array node, use + +`path_result json_path_get_distinct(json_t *json, const char *path);` + +instead. + +When a selection returns invalid list, it returns an empty list; When a selection returns invalid node is it returns NULL. + +### Expression + +| Operator | Description | +| -------- | ---------------------------------------------- | +| `==` | Equals to. | +| `!=` | Not equal to. | +| `>` | Greater than. | +| `>=` | Greater than or equal to. | +| `<` | Less than. | +| `<=` | Less than or equal to. | +| `=~` | Match a regular expression. Yet not supported. | +| `!` | Negate an expression. | +| `&&` | Logical AND. | +| `\|\|` | Logical OR. | + +Expressions are evaluated as json nodes. + +Logical operators have the higher priority than comparisons and results can be used as oprand of another logical operator. + +Comparisons only take paths and constants as oprand. + +Brackets `(` `)` can be used. diff --git a/lib/janssonpath/janssonpath.h b/lib/janssonpath/janssonpath.h new file mode 100644 index 0000000..25c1535 --- /dev/null +++ b/lib/janssonpath/janssonpath.h @@ -0,0 +1,18 @@ +#ifndef JANSSONPATH +#define JANSSONPATH + +#include + +// for simple jsonpath like $.a.b[0] we got a non-collection +// but for $[*] $[1:12], etc. we got a collection +// note distinct shuld be made dealing with intermidiate result +// so use path_result instead of json_t* +typedef struct path_result { + json_t *result; + int is_collection; +} path_result; + +json_t *json_path_get(json_t *json, const char *path); +path_result json_path_get_distinct(json_t *json, const char *path); + +#endif \ No newline at end of file diff --git a/lib/janssonpath/janssonpath_impl.c b/lib/janssonpath/janssonpath_impl.c new file mode 100644 index 0000000..f63b76d --- /dev/null +++ b/lib/janssonpath/janssonpath_impl.c @@ -0,0 +1,840 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "janssonpath.h" +#include "parse.h" + +#define TRUE 1 +#define FALSE 0 + +#define NEW(type) ((type *)malloc(sizeof(type))) +#define NEW_ARRAY(type, number) ((type *)malloc(sizeof(type) * (number))) +#define DELETE(obj) free((void *)obj) +#define DELETE_ARRAY(obj) free((void *)obj) + +#define debug_out(json) \ + do { \ + char *s = json_dumps(json, JSON_INDENT(2) | JSON_ENCODE_ANY); \ + if (s) { \ + puts(s); \ + free(s); \ + } else \ + puts("Empty"); \ + } while (0) + +static json_t *json_path_eval(json_t *root, json_t *curr, const char *begin, + const char *end); + +// all static functions here are internal implement. they follow the convention +// below: no check of arguments - pname/path must not be NULL; no accepting root +// note '$'; deal with [path, end) or from path till NULL encountered if +// end==NULL note that almost all jansson functions work fine with NULL input + +static json_t *json_path_get_all_properties(json_t *json) { + if (json_is_array(json)) + return json_incref(json); + else if (json_is_object(json)) { + json_t *ret = json_array(); + json_t *value; + const char *key; + json_object_foreach(json, key, value) { json_array_append(ret, value); } + return ret; + } else + return NULL; +} + +static path_result json_path_get_all_properties_col(path_result json) { + path_result result = {NULL, TRUE}; + size_t index; + json_t *iter; + if (json.is_collection) { + result.result = json_array(); + json_array_foreach(json.result, index, iter) { + json_t *property = json_path_get_all_properties(iter); + + json_array_extend(result.result, property); + json_decref(property); + } + } else { + result.result = json_path_get_all_properties(json.result); + } + return result; +} + +// remember to clean up stuffs before fail() +// note it can be recoverable fail - like property sellect on non-object when +// dealing with colleciton +#define fail() return result; + +typedef enum logic_operator { + logic_positive, + logic_negtive, + logic_and, + logic_or +} logic_operator; + +typedef enum comparison_operator { + comparison_nocmp, + comparison_eq, + comparison_ne, + comparison_gt, + comparison_ge, + comparison_lt, + comparison_le, + comparison_reg, // yet not supported + comparison_error +} comparison_operator; + +typedef struct sub_path { // sub_path does not own the string + const char *begin; + const char *end; +} sub_path; + +typedef struct comparison { + comparison_operator operator; + sub_path oprand[2]; // number of oprand is defined by operator +} comparison; + +struct logic_exp; // forward declartion +typedef struct expression { + union { + struct logic_exp *logic; + comparison *comp; + }; + enum tag_t { exp_logic, exp_compare, exp_error } tag; +} expression; + +typedef struct logic_exp { + logic_operator operator; + expression oprand[2]; // number of oprand is defined by operator +} logic_exp; + +// static json_t* eval_sub(json_t *root, json_t * curr, expression* exp){ +// return NULL; +// } + +#define remove_out_most_bar(begin, end) \ + do { \ + while (begin[0] == '(' && end[-1] == ')' && \ + (jassonpath_next_matched_bracket(begin, end, '(', ')') == \ + (end - 1))) { \ + begin++; \ + end--; \ + } \ + } while (0) + +static comparison *compile_compare(const char *begin, const char *end) { + comparison *result = NULL; + remove_out_most_bar(begin, end); + const char *comparison_first = + jassonpath_next_punctors_outside_para(begin, end, "=<>!"); + result = NEW(comparison); + if (comparison_first == end || !comparison_first[0]) { + result->operator= comparison_nocmp; + result->oprand[0].begin = begin; + result->oprand[0].end = end; + return result; + } + int len; + // technically uesr can do something like (@.a>@.b)==@.t + // however we ignore it for now + // simple and dumb + if (comparison_first[0] == '=' && comparison_first[1] == '=') { + result->operator= comparison_eq; + len = 2; + } else if (comparison_first[0] == '!' && comparison_first[1] == '=') { + result->operator= comparison_ne; + len = 2; + } else if (comparison_first[0] == '>' && comparison_first[1] != '=') { + result->operator= comparison_gt; + len = 1; + } else if (comparison_first[0] == '>' && comparison_first[1] == '=') { + result->operator= comparison_ge; + len = 2; + } else if (comparison_first[0] == '<' && comparison_first[1] != '=') { + result->operator= comparison_lt; + len = 1; + } else if (comparison_first[0] == '<' && comparison_first[1] == '=') { + result->operator= comparison_le; + len = 2; + } else if (comparison_first[0] == '=' && comparison_first[1] == '~') { + result->operator= comparison_reg; + len = 2; + } else { + result->operator= comparison_error; + fail(); + } + + const char *lhss = begin, *lhse = comparison_first, + *rhss = comparison_first + len, *rhse = end; + remove_out_most_bar(lhss, lhse); + remove_out_most_bar(rhss, rhse); + result->oprand[0].begin = lhss; + result->oprand[0].end = lhse; + result->oprand[1].begin = rhss; + result->oprand[1].end = rhse; + return result; +} + +static expression compile_expression(const char *begin, const char *end) { + expression result = {{NULL}, exp_error}; + remove_out_most_bar(begin, end); + const char *logic_first = + jassonpath_next_punctors_outside_para(begin, end, "|&"); + // no binary logic operator found + if (logic_first == end || !*logic_first) { + // negative + if (begin[0] == '!') { + result.tag = exp_logic; + result.logic = NEW(logic_exp); + result.logic->operator= logic_negtive; + result.logic->oprand[0] = compile_expression(begin + 1, end); + } else { // comparison + result.tag = exp_compare; + result.comp = compile_compare(begin, end); + } + return result; + } + // logic operator found + if (logic_first[0] != logic_first[1]) fail(); //&& or || + result.tag = exp_logic; + result.logic = NEW(logic_exp); + result.logic->operator= logic_first[0] == '&' ? logic_and : logic_or; + result.logic->oprand[0] = compile_expression(begin, logic_first); + result.logic->oprand[1] = compile_expression(logic_first + 2, end); + return result; +} + +static void comparison_free(comparison *exp) { DELETE(exp); } + +static void expression_free(expression exp); + +static void logic_exp_free(logic_exp *exp) { + expression_free(exp->oprand[0]); + if (exp->operator> logic_negtive) expression_free(exp->oprand[1]); + DELETE(exp); +} + +static void expression_free(expression exp) { + if (exp.tag == exp_logic) + logic_exp_free(exp.logic); + else + comparison_free(exp.comp); +} + +static path_result json_path_get_property(json_t *curr, const char *begin, + const char *end) { + path_result result = {NULL, FALSE}; + if (begin[0] == '*') { // select all members + result.is_collection = TRUE; + if (begin[1] && begin + 1 != end) { + result.result = json_array(); + fail(); + } + result.result = json_path_get_all_properties(curr); + return result; + } else if (begin[0] == '.') { + if (begin[1] && begin + 1 != end) fail(); + result.result = json_array(); + result.is_collection = TRUE; + path_result out_layer = {json_array(), TRUE}; + json_array_append(out_layer.result, curr); + + json_array_append(result.result, curr); // including itself + + while (out_layer.result && !(json_is_array(out_layer.result) && + !json_array_size(out_layer.result))) { + path_result cur_layer = json_path_get_all_properties_col(out_layer); + json_decref(out_layer.result); + json_array_extend(result.result, cur_layer.result); + out_layer = cur_layer; + } + + json_decref(out_layer.result); + return result; + } else if (begin[0] == '#') { + if (begin[1] && begin + 1 != end) fail(); + + size_t len; + if (json_is_array(curr)) + len = json_array_size(curr); + else + len = 0; + result.result = json_integer(len); + result.is_collection = FALSE; + return result; + } + + // number or named property + const char *seg[4]; + seg[0] = begin; + size_t segn = 1; // number of parameters seprated by ':' + while (segn < 4 && seg[segn - 1] != end && seg[segn - 1][0]) { + seg[segn] = jassonpath_next_seprator(seg[segn - 1], end, ':'); + if (seg[segn] == end || !*seg[segn]) // not found + break; + else { + seg[segn]++; + segn++; + } + } + seg[segn] = end; + long seg_int[3]; + int seg_filled[3]; // is it a number and been filled? + { // make a scope to isolate i. workaround for poor supported inline + // declaration in for + size_t i; + for (i = 0; i < segn; i++) { + char *end_of_int; + if ((seg[i] != end && seg[i][0])) { + seg_int[i] = strtol(seg[i], &end_of_int, 10); + seg_filled[i] = (end_of_int != seg[i]); + } else + seg_filled[i] = FALSE; + // if(seg_filled[i]&&end_of_int!=seg[i+1]) ; error, but we ignore + // that + } + } + // only one parameter and it's not a number + if (segn == 1 && !seg_filled[0]) { // select named properties + result.is_collection = FALSE; + if (!json_is_object(curr)) fail(); + const char *pname = jassonpath_strdup_no_terminal(begin, end); + result.result = json_incref(json_object_get(curr, pname)); + free((void *)pname); + } else if (segn == 1 && seg_filled[0]) { //[i] and [-i] + result.is_collection = FALSE; + if (!json_is_array(curr) || !seg_filled[0]) fail(); + size_t index; + if (seg_int[0] >= 0 && seg[0][0] != '-') + index = seg_int[0]; + else + index = json_array_size(curr) - (-seg_int[0] + 1); + result.result = json_incref(json_array_get(curr, index)); + } else { //[i:j]([:j] [i:]) or [i:j:k] or [-i:] + result.is_collection = TRUE; + result.result = json_array(); + if (!json_is_array(curr)) fail(); // incorrect input + long step = 1; + size_t index_begin = 0; + size_t index_end = json_array_size(curr); + int rev = + (segn == 2 && seg_filled[0] && !seg_filled[1] && seg_int[0] < 0); + if (!rev) { + if (seg_filled[0] && seg_int[0] >= 0) index_begin = seg_int[0]; + if (segn == 2 && seg_filled[1] && ((size_t)seg_int[1]) < index_end) + index_end = seg_int[1]; + else if (segn == 3 && seg_filled[2] && + ((size_t)seg_int[2]) < index_end) + index_end = seg_int[2]; + if (segn == 3 && seg_filled[1] && seg_filled[1] != 0) + step = seg_int[1]; + // negative step does not mean any thing + // because we do not arrange output with order + if (step < 0) step = -step; + } else { + long index_begin_l = + index_end + seg_int[0]; // index_begin=index_end-i + if (index_begin_l >= 0) index_begin = index_begin_l; + } + size_t i; + for (i = index_begin; i < index_end; i += step) { + json_t *ele = json_array_get(curr, i); + json_array_append_new(result.result, ele); + } + } + return result; +} + +static json_t *json_index_json(json_t *json, const json_t *index) { + json_t *sel; + size_t i = 0; + int named = FALSE; + if (json_is_integer(index)) { + i = json_integer_value(index); + } else if (json_is_true(index)) { + i = 1; + } else if (json_is_false(index) || json_is_null(index)) { + i = 0; + } else if (json_is_string(index)) { + named = TRUE; + } + if (named) + sel = json_object_get(json, json_string_value(index)); + else + sel = json_array_get(json, i); + return sel; +} + +static int json_to_bool(json_t *json) { + if (!json) return FALSE; + return json_is_true(json) || + (json_is_integer(json) && json_integer_value(json)) || + (json_is_string(json) && !strcmp("true", json_string_value(json))); +} + +#define OCT_DIGIT_NOTE (CHAR_MAX) +#define HEX_DIGIT_NOTE (CHAR_MAX - 1) + +static char unescape_map[UCHAR_MAX] = { + ['a'] = '\a', + ['b'] = '\b', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['t'] = '\t', + ['v'] = '\v', + ['\''] = '\'', + ['"'] = '"', + ['?'] = '?', + ['\\'] = '\\', + + ['0'] = OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, + OCT_DIGIT_NOTE, // 0-7 + ['x'] = HEX_DIGIT_NOTE, +}; + +static const char *unescape(const char *begin, const char *end) { + char *ret = NEW_ARRAY(char, end - begin + 1); + char *iter = ret; + const char *src_iter; + for (src_iter = begin; src_iter != end && *src_iter; ++iter) { + if (*src_iter != '\\') { + *iter = *src_iter; + ++src_iter; + } else { + ++src_iter; + if (src_iter == end) { + DELETE_ARRAY(ret); + return NULL; + } + char maped = unescape_map[(unsigned char)*src_iter]; + switch (maped) { + case '\0': { + DELETE_ARRAY(ret); + return NULL; + } + case OCT_DIGIT_NOTE: { + char oct = 0; + int count = 0; + while (src_iter != end && + unescape_map[(unsigned char)*src_iter] == + OCT_DIGIT_NOTE && + count < 3) { + oct *= 8; + oct += *src_iter - '0'; + ++src_iter; + ++count; + } + *iter = oct; + } break; + case HEX_DIGIT_NOTE: { + ++src_iter; + if (src_iter == end) { + DELETE_ARRAY(ret); + return NULL; + } //'x' + char hex = 0; + int count = 0; + while (src_iter != end && isxdigit(*src_iter) && count < 2) { + hex *= 16; + int digit; + if (isdigit(*src_iter)) + digit = *src_iter - '0'; + else if (*src_iter > 'a' && *src_iter < 'f') + digit = *src_iter - 'a'; + else if (*src_iter > 'A' && *src_iter < 'F') + digit = *src_iter - 'A'; + else + assert(0); + hex += digit; + ++src_iter; + ++count; + } + if (count == 0) { + DELETE_ARRAY(ret); + return NULL; + } + *iter = hex; + } break; + default: + *iter = maped; + ++src_iter; + } + } + } + *iter = '\0'; + return ret; +} + +#undef OCT_DIGIT_NOTE +#undef HEX_DIGIT_NOTE + +static json_t *json_literal_from_string(const char *begin, const char *end) { + json_t *result = NULL; + if (begin[0] == '\"' && end[-1] == '\"' && (end - begin) > 1) { + const char *value_str = unescape(begin + 1, end - 1); + result = json_string(value_str); + DELETE_ARRAY(value_str); + } else if (isdigit(begin[0]) || begin[0] == '-') { + char *end_of_number; + long ret = strtol(begin, &end_of_number, 10); + if (end == end_of_number || !*end_of_number) + result = json_integer(ret); + else { + double retd = strtod(begin, &end_of_number); + if (end == end_of_number || !*end_of_number) + result = json_real(retd); + } + } else { + const char *str = jassonpath_strdup_no_terminal(begin, end); + if (!strcmp("true", str)) + result = json_true(); + else if (!strcmp("false", str)) + result = json_false(); + else if (!strcmp("null", str)) + result = json_null(); + DELETE_ARRAY(str); + } + return result; +} + +static path_result json_path_get_impl(json_t *root, path_result curr, + const char *path, const char *end); + +static json_t *json_path_eval(json_t *root, json_t *curr, const char *begin, + const char *end) { + if (begin[0] != '$' && begin[0] != '@' && begin[0] != '.') { + return json_literal_from_string(begin, end); + }; + // sub path + path_result curr_p = {curr, FALSE}; + path_result root_p = {root, FALSE}; + if (begin[0] == '@') + return json_path_get_impl(root, curr_p, begin + 1, end).result; + else if (begin[0] == '$') + return json_path_get_impl(root, root_p, begin + 1, end).result; + else + return json_path_get_impl(root, curr_p, begin, end) + .result; // default to current node +} + +static double json_to_double(const json_t *json) { + if (json_is_real(json)) + return json_real_value(json); + else if (json_is_string(json)) { + const char *begin = json_string_value(json); + char *end; + double value = strtod(begin, &end); + if (end == begin + json_string_length(json)) + return value; + else + return NAN; + } else if (json_is_integer(json)) { + return json_integer_value(json); + } else if (json_is_boolean(json)) { + return json_boolean_value(json) ? 1.0 : 0.0; + } else if (json_is_null(json)) { + return 0.0; + } + // else if(json_is_object(json)||json_is_array(json)) + return NAN; +} + +static long long json_to_int(const json_t *json) { + static const long long error = (long long)(INT_MAX) + 1; + if (json_is_real(json)) + return json_real_value(json); + else if (json_is_string(json)) { + const char *begin = json_string_value(json); + char *end; + long value = strtol(begin, &end, 10); + if (end == begin + json_string_length(json)) + return value; + else + return error; + } else if (json_is_integer(json)) { + return json_integer_value(json); + } else if (json_is_boolean(json)) { + return json_boolean_value(json) ? 1 : 0; + } else if (json_is_null(json)) { + return 0; + } + // else if(json_is_object(json)||json_is_array(json)) + return error; +} + +int comp_diff(double diff, comparison_operator op) { + int result; + if (!diff) + result = (op == comparison_eq) || (op == comparison_ge) || + (op == comparison_le); + else if (diff > 0.0) + result = (op == comparison_ne) || (op == comparison_gt) || + (op == comparison_ge); + else + result = (op == comparison_ne) || (op == comparison_lt) || + (op == comparison_le); + return result; +} + +static json_t *json_compare(comparison_operator op, json_t *lhs, json_t *rhs) { + if (op == comparison_nocmp || op == comparison_error || + op == comparison_reg) + return NULL; // should not happen + if (!lhs || !rhs) return json_false(); + if (json_is_array(lhs) || json_is_array(rhs) || json_is_object(lhs) || + json_is_object(rhs)) + return json_false(); + + if (json_is_null(lhs) || + json_is_null( + rhs)) { // null should not be equal to anything other than null + if (op != comparison_eq && op != comparison_ne) return json_false(); + return json_boolean((json_is_null(lhs) && json_is_null(rhs)) == + (op == comparison_eq)); + } + // conversion: string->boolean->integer->number string->integer->number + // however "true" should not be treat as 1 + if (json_is_number(lhs) || json_is_number(rhs) || + (op != comparison_eq && op != comparison_ne)) { // numeric compare + if (json_is_real(lhs) || json_is_real(rhs)) { + double lhs_d = json_to_double(lhs); + double rhs_d = json_to_double(rhs); + if (isnan(lhs_d) || isnan(rhs_d)) return json_false(); + return json_boolean(comp_diff(lhs_d - rhs_d, op)); + } else { + long lhs_d = json_to_int(lhs); + long rhs_d = json_to_int(rhs); + if (lhs_d > INT_MAX || rhs_d > INT_MAX) return json_false(); + return json_boolean(comp_diff(lhs_d - rhs_d, op)); + } + } else if (json_is_boolean(lhs) || json_is_boolean(rhs)) { + int lhs_b = json_to_bool(lhs); + int rhs_b = json_to_bool(rhs); + lhs_b = lhs_b ? 1 : 0; + rhs_b = rhs_b ? 1 : 0; // normalization + return json_boolean((lhs_b == rhs_b) == (op == comparison_eq)); + } else if (json_is_string(lhs) && json_is_string(rhs)) { // string compare + int a = strcmp(json_string_value(lhs), json_string_value(rhs)); + return json_boolean(a == (op == comparison_ne)); + } + return json_true(); +} + +static json_t *execute_compare(comparison *comp, json_t *root, json_t *curr) { + json_t *result = NULL; + switch (comp->operator) { + case comparison_nocmp: + result = json_path_eval(root, curr, comp->oprand[0].begin, + comp->oprand[0].end); + break; + case comparison_eq: + case comparison_ne: + case comparison_gt: + case comparison_ge: + case comparison_lt: + case comparison_le: { + json_t *lhs = json_path_eval(root, curr, comp->oprand[0].begin, + comp->oprand[0].end); + json_t *rhs = json_path_eval(root, curr, comp->oprand[1].begin, + comp->oprand[1].end); + result = json_compare(comp->operator, lhs, rhs); + json_decref(lhs); + json_decref(rhs); + } break; + case comparison_reg: // yet not supported + case comparison_error: + default: + result = NULL; + } + return result; +} + +static json_t *execute_exp(expression exp, json_t *root, json_t *curr) { + if (exp.tag == exp_logic) { + logic_exp *logic = exp.logic; + if (logic->operator== logic_positive || logic->operator== + logic_negtive) { + json_t *ret = execute_exp(logic->oprand[0], root, curr); + int b = json_to_bool(ret); + json_decref(ret); + if (logic->operator== logic_negtive) b = !b; + return json_boolean(b); + } else if (logic->operator== logic_and || logic->operator== logic_or) { + json_t *ret1 = execute_exp(logic->oprand[0], root, curr), + *ret2 = execute_exp(logic->oprand[1], root, curr); + int b1 = json_to_bool(ret1), b2 = json_to_bool(ret2); + json_decref(ret1); + json_decref(ret2); + int r; + if (logic->operator== logic_and) + r = b1 && b2; + else + r = b1 || b2; + return json_boolean(r); + } + } else if (exp.tag == exp_compare) { + return execute_compare(exp.comp, root, curr); + } + return NULL; +} + +static path_result json_path_get_property_col(json_t *root, path_result curr, + const char *name, + const char *end) { + path_result result = {NULL, FALSE}; + size_t index; + json_t *iter; + + int cond = (name[0] == '?'); + if (name[0] == '(' || + (cond && name[1] == '(')) { // expression [?(..)] [(..)] + if (cond) name++; + const char *right = + jassonpath_next_matched_bracket(name, end, '(', ')'); + if (right != end - 1) fail(); + expression exp = compile_expression(name, end); + + if (exp.tag == exp_error) fail(); + + if (cond) { + json_t *properties = json_path_get_all_properties_col(curr) + .result; // unify properties into array + + result.result = json_array(); + result.is_collection = TRUE; + json_array_foreach(properties, index, iter) { + json_t *exc_result = execute_exp(exp, root, iter); + int selected = json_to_bool(exc_result) || + (json_is_string(exc_result) && + json_string_length( + exc_result)); // special case for path like + // "$.store.book[?(@.isbn)]" + if (selected) json_array_append(result.result, iter); + json_decref(exc_result); + } + json_decref(properties); + } else { + if (curr.is_collection) { + result.result = json_array(); + result.is_collection = TRUE; + json_array_foreach(curr.result, index, iter) { + json_t *exc_result = execute_exp(exp, root, iter); + json_t *sel = json_index_json(iter, exc_result); + json_array_append(result.result, sel); + json_decref(sel); + json_decref(exc_result); + } + } else { + json_t *exc_result = execute_exp(exp, root, curr.result); + result.result = json_index_json(curr.result, exc_result); + result.is_collection = FALSE; + json_decref(exc_result); + } + } + + expression_free(exp); + return result; + } + + if (curr.is_collection) { + result.result = json_array(); + json_array_foreach(curr.result, index, iter) { + path_result r = json_path_get_property(iter, name, end); + if (r.is_collection) + json_array_extend(result.result, r.result); + else + json_array_append(result.result, r.result); + json_decref(r.result); + } + result.is_collection = TRUE; + } else { + result = json_path_get_property(curr.result, name, end); + } + return result; +} + +// on_collection if we are path_get on a collection +// we treat . and [] the same way as property +// and by this we can simplify the implementation a lot +static path_result json_path_get_impl(json_t *root, path_result curr, + const char *path, const char *end) { + if (!curr.result || path == end || + *path == '\0') { // no selection to do. return original collection + json_incref( + curr.result); // note this should looks like a copy to users + return curr; + } + path_result result = {NULL, FALSE}; + + if (!(path[0] == '.' || path[0] == '[')) fail(); + + const char *pname = path + 1; + const char *pname_end; + // got the full property name + if (path[0] == '[') { + pname_end = jassonpath_next_matched_bracket(path, end, '[', ']'); + if (pname_end[0] != ']') fail(); + path = pname_end + 1; + } else if (path[1] == '.') { + pname_end = path + 2; + path = pname_end; + } else { + pname_end = jassonpath_next_delima(pname, end); + path = pname_end; + } + + const char *name_begin, *name_end; + // if it's string, deal with the string problems + if (*pname == '\"') { + if (pname_end == pname || pname_end[-1] != '\"') fail(); + pname++; + pname_end--; + name_begin = unescape(pname, pname_end); + } else { + name_begin = jassonpath_strdup_no_terminal(pname, pname_end); + } + name_end = name_begin + (pname_end - pname); + + result = json_path_get_property_col(root, curr, name_begin, name_end); + DELETE_ARRAY(name_begin); + path_result ret = json_path_get_impl(root, result, path, end); + json_decref(result.result); + return ret; +} + +// json_path_get, +// version with distinction between collection and simple result +path_result json_path_get_distinct(json_t *json, const char *path) { + path_result result = {NULL, FALSE}; + if (!path || !json) fail(); + if (path[0] == '$') + path++; // it can be omited safely since current node is the root node + // note that every json path selection is applyed to a collection of json + // nodes + path_result cur = {json, FALSE}; + result = json_path_get_impl(json, cur, path, NULL); + return result; +} + +// return NULL if either of arguments is NULL or any error occurs. +// path must be at least zero length and terminated with '\0' +// assuming no extra spaces except for string +// users are reponsible to decref the json_node returned +// extention to standard jsonpath - $."0" $.0 $[0] $["0"] they are all the +// same(for simplify implementation) +json_t *json_path_get(json_t *json, const char *path) { + return json_path_get_distinct(json, path).result; +} \ No newline at end of file diff --git a/lib/janssonpath/parse.c b/lib/janssonpath/parse.c new file mode 100644 index 0000000..258cbff --- /dev/null +++ b/lib/janssonpath/parse.c @@ -0,0 +1,88 @@ +#include +#include +#include +// functions for parsing + +const char *jassonpath_match_string(const char *begin, const char *end) { + if (*begin == '\"') + begin++; // begin should be left quotation mark to match + else + return begin; + for (; begin != end && *begin; begin++) { + if (begin[0] == '\\' && begin[1] != '\0') + begin += 2; + else if (begin[0] == '\"') { + begin++; + break; + } + } + return begin; +} + +const char *jassonpath_next_delima(const char *begin, const char *end) { + for (; begin != end && *begin; begin++) { + if (begin[0] == '\"') begin = jassonpath_match_string(begin, end); + if (begin[0] == '[' || begin[0] == ']' || begin[0] == '.') break; + } + return begin; +} + +const char *jassonpath_next_matched_bracket(const char *begin, const char *end, + char left, char right) { + if (*begin == left) + begin++; // begin should be left bracket to match + else + return begin; + int count = 1; + for (; begin != end && *begin; begin++) { + if (begin[0] == '\"') begin = jassonpath_match_string(begin, end); + if (begin[0] == left) + count++; + else if (begin[0] == right) + count--; + if (!count) break; + } + return begin; +} + +const char *jassonpath_next_seprator(const char *begin, const char *end, + char sep) { + for (; begin != end && *begin; begin++) { + if (begin[0] == '\"') begin = jassonpath_match_string(begin, end); + if (begin[0] == sep) break; + } + return begin; +} + +const char *jassonpath_next_punctors_outside_para(const char *begin, + const char *end, char *sep) { + for (; begin != end && *begin; begin++) { + if (begin[0] == '\"') begin = jassonpath_match_string(begin, end); + if (begin[0] == '(') + begin = jassonpath_next_matched_bracket(begin, end, '(', ')'); + else if (strchr(sep, begin[0])) + break; + } + return begin; +} + +const char *jassonpath_next_punctor_outside_para(const char *begin, + const char *end, char sep) { + for (; begin != end && *begin; begin++) { + if (begin[0] == '\"') begin = jassonpath_match_string(begin, end); + if (begin[0] == '(') + begin = jassonpath_next_matched_bracket(begin, end, '(', ')'); + else if (begin[0] == sep) + break; + } + return begin; +} + +const char *jassonpath_strdup_no_terminal(const char *begin, const char *end) { + size_t len = end - begin; + if (!end) len = strlen(begin); + char *ret = (char *)malloc(sizeof(char) * (len + 1)); + memcpy(ret, begin, len); + ret[len] = '\0'; + return ret; +} diff --git a/lib/janssonpath/parse.h b/lib/janssonpath/parse.h new file mode 100644 index 0000000..3ae57c0 --- /dev/null +++ b/lib/janssonpath/parse.h @@ -0,0 +1,15 @@ +#ifndef JANSSONPATH_PARSE +#define JANSSONPATH_PARSE +// functions for parsing +const char *jassonpath_match_string(const char *begin, const char *end); +const char *jassonpath_next_delima(const char *begin, const char *end); +const char *jassonpath_next_matched_bracket(const char *begin, const char *end, + char left, char right); +const char *jassonpath_next_seprator(const char *begin, const char *end, + char sep); +const char *jassonpath_strdup_no_terminal(const char *begin, const char *end); +const char *jassonpath_next_punctor_outside_para(const char *begin, + const char *end, char sep); +const char *jassonpath_next_punctors_outside_para(const char *begin, + const char *end, char *sep); +#endif diff --git a/lib/janssonpath/test/Makefile b/lib/janssonpath/test/Makefile new file mode 100644 index 0000000..6989065 --- /dev/null +++ b/lib/janssonpath/test/Makefile @@ -0,0 +1,9 @@ +DBG_FLG = -Wall -Wextra -pedantic -g3 -Og -D_DEBUG -fsanitize=address -fno-omit-frame-pointer +CFLAGS = -std=c11 $(DBG_FLG) +LDFLAGS = -lasan -ljansson + +test:test.c ../janssonpath.o + cc $(CFLAGS) $? -o $@ $(LDFLAGS) + +../janssonpath.o: ../janssonpath_impl.o ../parse.o + ld -r $? -o $@ \ No newline at end of file diff --git a/lib/janssonpath/test/test.c b/lib/janssonpath/test/test.c new file mode 100644 index 0000000..d53f5bb --- /dev/null +++ b/lib/janssonpath/test/test.c @@ -0,0 +1,19 @@ +#include "jansson.h" +#include "../janssonpath.h" +#include + +int main(int argn,char**argv){ + if(argn<3)return -1; + puts("path:"); + puts(argv[2]); + json_error_t error; + json_t * json=json_load_file(argv[1],0,&error); + json_t *result=json_path_get(json,argv[2]); + char * r=json_dumps(result,JSON_INDENT(2)|JSON_ENCODE_ANY); + puts("result:"); + if(r)puts(r); + json_decref(result); + json_decref(json); + free(r); + return 0; +} diff --git a/lib/janssonpath/test/test.json b/lib/janssonpath/test/test.json new file mode 100644 index 0000000..681f082 --- /dev/null +++ b/lib/janssonpath/test/test.json @@ -0,0 +1,37 @@ +{ + "store": { + "book": [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + }, + "expensive": 10 +} \ No newline at end of file diff --git a/lib/jmespath/.gitignore b/lib/jmespath/.gitignore deleted file mode 100644 index 328f8ec..0000000 --- a/lib/jmespath/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -**/target -jmespath/Cargo.lock -.idea/** \ No newline at end of file diff --git a/lib/jmespath/CHANGELOG.md b/lib/jmespath/CHANGELOG.md deleted file mode 100644 index c62b8a2..0000000 --- a/lib/jmespath/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# CHANGELOG - -## 0.2.0 - 2017-09-26 - -* Now works with Serde 1.0: - https://github.com/jmespath/jmespath.rs/pull/24 -* impl `Error` for `JmespathError`: - https://github.com/jmespath/jmespath.rs/pull/22 -* Removed parentheses around `Fn` to fix syntactic error: - https://github.com/jmespath/jmespath.rs/pull/20 - -## 0.1.1 - 2017-01-07 - -* Now works on stable by placing specialization behind the `specialized` - feature flag. https://github.com/jmespath/jmespath.rs/pull/19 - -## 0.1.0 - 2016-12-15 - -* First somewhat stable release. - -## 0.0.1 - 2016-01-30 - -* Initial release. diff --git a/lib/jmespath/README.md b/lib/jmespath/README.md deleted file mode 100644 index 8a4774a..0000000 --- a/lib/jmespath/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# JMESPath for Rust - -Rust implementation of [JMESPath](http://jmespath.org), a query language for JSON. - -[Documentation](https://docs.rs/jmespath/) - -## Installing - -This crate is [on crates.io](https://crates.io/crates/jmespath) and can be used -by adding `jmespath` to the dependencies in your project's `Cargo.toml`. - -```toml -[dependencies] -jmespath = "^0.3.0" -``` - -If you are using a nightly compiler, or reading this when specialization in Rust -is stable (see [rust#31844](https://github.com/rust-lang/rust/issues/31844)), then -enable the `specialized` feature to switch on usage of specialization to get more -efficient code: - -```toml -[dependencies.jmespath] -version = "^0.3.0" -features = ["specialized"] -``` - -## Examples - -```rust -extern crate jmespath; - -let expr = jmespath::compile("foo.bar").unwrap(); - -// Parse some JSON data into a JMESPath variable -let json_str = r#"{"foo": {"bar": true}}"#; -let data = jmespath::Variable::from_json(json_str).unwrap(); - -// Search the data with the compiled expression -let result = expr.search(data).unwrap(); -assert_eq!(true, result.as_boolean().unwrap()); -``` diff --git a/lib/jmespath/impl/Cargo.lock b/lib/jmespath/impl/Cargo.lock deleted file mode 100644 index 220fef1..0000000 --- a/lib/jmespath/impl/Cargo.lock +++ /dev/null @@ -1,198 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "deunicode" -version = "1.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "jmespath" -version = "0.3.0" -dependencies = [ - "lazy_static", - "serde", - "serde_json", - "slug", -] - -[[package]] -name = "jmespathimpl" -version = "0.1.0" -dependencies = [ - "jmespath", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "serde" -version = "1.0.201" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.201" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "slug" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - -[[package]] -name = "syn" -version = "2.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" diff --git a/lib/jmespath/impl/Cargo.toml b/lib/jmespath/impl/Cargo.toml deleted file mode 100644 index 6a97831..0000000 --- a/lib/jmespath/impl/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "jmespathimpl" -version = "0.1.0" -edition = "2021" - - -[lib] -path = "src/lib.rs" -crate-type = ["staticlib"] - -[dependencies] -jmespath = { path = "../jmespath" } diff --git a/lib/jmespath/impl/src/lib.rs b/lib/jmespath/impl/src/lib.rs deleted file mode 100644 index ec20d80..0000000 --- a/lib/jmespath/impl/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -use jmespath; -use jmespath::Expression; -use jmespath::Variable; - -use std::ffi::CStr; -use std::ffi::CString; -use std::os::raw::c_char; - -#[no_mangle] -pub extern "C" fn jmespath_filter_json(json: *const c_char, filter: *const c_char) -> *mut c_char { - let c_json: &CStr = unsafe { CStr::from_ptr(json) }; - let c_filter: &CStr = unsafe { CStr::from_ptr(filter) }; - - let rust_json: &str = c_json.to_str().unwrap(); - let rust_filter: &str = c_filter.to_str().unwrap(); - - let expr: Expression = jmespath::compile(rust_filter).unwrap(); - - // Parse some JSON data into a JMESPath variable - let data: Variable = jmespath::Variable::from_json(rust_json).unwrap(); - - // Search the data with the compiled expression - let result: String = expr.search(data).unwrap().to_string(); - - let c_result: CString = CString::new(result).unwrap(); - return c_result.into_raw(); -} diff --git a/lib/jmespath/jmespath/Cargo.toml b/lib/jmespath/jmespath/Cargo.toml deleted file mode 100644 index f7f4451..0000000 --- a/lib/jmespath/jmespath/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "jmespath" -version = "0.3.0" -authors = ["Michael Dowling "] -description = "Rust implementation of JMESPath, a query language for JSON" -readme = "../README.md" -keywords = ["json", "jmespath", "query"] -homepage = "https://github.com/jmespath/jmespath.rs" -repository = "https://github.com/jmespath/jmespath.rs" -documentation = "https://docs.rs/jmespath/" -license = "MIT" -build = "build.rs" -edition = "2018" - -[dependencies] -serde = { version = "1", features = ["rc"] } -serde_json = "1" -lazy_static = "1.4" - -[build-dependencies] -serde_json = "1" -slug = "0.1" - -[dev-dependencies] -bencher = "0.1.5" -serde_derive = "1" - -[[bench]] -name = "generated" -harness = false - -[features] -# `sync` utilizes an Arc instead of an Rc for JMESPath runtime variables. -# Using an Arc allows you to share compiled expressions across threads. -sync = [] -# `specialized` enables the use of specialization for more efficient code -# however at time of writing it is unstable & so requires a nightly compiler. -# See https://github.com/rust-lang/rust/issues/31844 for the latest status. -specialized = [] diff --git a/lib/jmespath/jmespath/benches/generated.rs b/lib/jmespath/jmespath/benches/generated.rs deleted file mode 100644 index 27db9f8..0000000 --- a/lib/jmespath/jmespath/benches/generated.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Generated benchmarks. Benchmarks are generated in build.rs and are -//! sourced from tests/compliance/benchmarks.json -//#![feature(test)] - -//extern crate bencher; - -use bencher::*; -use jmespath::{compile, parse, Rcvar, Variable}; - -include!(concat!(env!("OUT_DIR"), "/benches.rs")); diff --git a/lib/jmespath/jmespath/build.rs b/lib/jmespath/jmespath/build.rs deleted file mode 100644 index 0e7b475..0000000 --- a/lib/jmespath/jmespath/build.rs +++ /dev/null @@ -1,255 +0,0 @@ -//! Generates compliance tests and benchmarks - -use std::env; -use std::fs::{self, File}; -use std::io::{Read, Write}; -use std::path::Path; - -use serde_json::Value; - -pub fn main() { - let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR not specified"); - let bench_path = Path::new(&out_dir).join("benches.rs"); - let mut bench_file = File::create(&bench_path).expect("Could not create file"); - let compliance_path = Path::new(&out_dir).join("compliance_tests.rs"); - let mut compliance_file = File::create(&compliance_path).expect("Could not create file"); - let suites = load_test_suites(); - - let mut all_benches_test = vec![]; - - for (suite_num, &(ref filename, ref suite)) in suites.iter().enumerate() { - let suite_obj = suite.as_object().expect("Suite not object"); - let given = suite_obj.get("given").expect("No given value"); - let cases = suite_obj.get("cases").expect("No cases value"); - let short_filename = filename - .replace(".json", "") - .replace("tests/compliance/", ""); - let given_string = serde_json::to_string(given).unwrap(); - for (case_num, case) in cases - .as_array() - .expect("cases not array") - .iter() - .enumerate() - { - let case_obj = case.as_object().expect("case not object"); - if case_obj.get("bench").is_some() { - generate_bench( - &mut all_benches_test, - &short_filename, - suite_num, - case_num, - case_obj, - &given_string, - &mut bench_file, - ); - } else { - generate_test( - &short_filename, - suite_num, - case_num, - case_obj, - &given_string, - &mut compliance_file, - ); - } - } - } - bench_file - .write_all( - format!( - r#" -benchmark_group!(benches, {}); -benchmark_main!(benches); -"#, - all_benches_test.join(", ") - ) - .as_bytes(), - ) - .expect("Error bench headers"); -} - -/// Load all tests suites found in the tests/compliance directory. -pub fn load_test_suites() -> Vec<(String, Value)> { - let mut result = vec![]; - let files = fs::read_dir("tests/compliance").expect("Invalid directory: tests/compliance"); - for filename in files { - let path = filename.expect("Invalid file").path(); - let file_path = path.to_str().expect("Could not to_str file").to_string(); - let mut f = File::open(path).expect("Unable to open file"); - let mut file_data = String::new(); - f.read_to_string(&mut file_data) - .expect("Could to read JSON to string"); - let mut suite_json: Value = serde_json::from_str(&file_data).expect("invalid JSON"); - let suites = suite_json - .as_array_mut() - .expect("Test suite is not a JSON array"); - while let Some(suite) = suites.pop() { - result.push((file_path.clone(), suite)); - } - } - result -} - -/// Gets the expression from a test case with helpful error messages. -#[inline] -fn get_expr(case: &serde_json::Map) -> &str { - case.get("expression") - .expect("No expression in case") - .as_str() - .expect("Could not convert case to string") -} - -/// SLugifies a string for use as a function name. -/// Ensures that the generated slug is truncated if too long. -#[inline] -fn slugify(s: &str) -> String { - let mut s = slug::slugify(s).replace("-", "_"); - if s.len() > 25 { - s.truncate(25); - } - s -} - -/// Generates a function name for a test suite's test case. -#[inline] -fn generate_fn_name( - filename: &str, - suite_num: usize, - case_num: usize, - case: &serde_json::Map, -) -> String { - let expr = get_expr(case); - // Use the comment as the fn description if it is present. - let description = match case.get("comment") { - Some(c) => c.as_str().expect("comment is not a string"), - None => expr, - }; - format!( - "{}_{}_{}_{}", - slugify(filename), - suite_num, - case_num, - slugify(description) - ) -} - -/// Generates a benchmark to be run with cargo bench. -/// -/// Each test case will generate a case for parsing and a case for both -/// parsing and interpreting. -fn generate_bench( - all_tests: &mut Vec, - filename: &str, - suite_num: usize, - case_num: usize, - case: &serde_json::Map, - given_string: &str, - f: &mut File, -) { - let expr = get_expr(case); - let expr_string = expr.replace("\"", "\\\""); - let fn_suffix = generate_fn_name(filename, suite_num, case_num, case); - let bench_type = case - .get("bench") - .unwrap() - .as_str() - .expect("bench is not a string"); - - // Validate that the bench attribute is an expected type. - if bench_type != "parse" && bench_type != "full" && bench_type != "interpret" { - panic!("invalid bench type: {}", bench_type); - } - - // Create the parsing benchmark if "parse" or "full" - if bench_type == "parse" || bench_type == "full" { - let test_fn_name = format!("{}_parse_lex", fn_suffix); - f.write_all( - format!( - r##" - -fn {}(b: &mut Bencher) {{ - b.iter(|| {{ parse({:?}).ok() }}); -}} - -"##, - test_fn_name, expr_string - ) - .as_bytes(), - ) - .expect("Error writing parse benchmark"); - all_tests.push(test_fn_name) - } - - // Create the interpreter benchmark if "interpret" or "full" - if bench_type == "interpret" || bench_type == "full" { - let test_fn_name = format!("{}_interpret", fn_suffix); - f.write_all( - format!( - r##" - -fn {}(b: &mut Bencher) {{ - let data = Rcvar::new(Variable::from_json({:?}).expect("Invalid JSON given")); - let expr = compile({:?}).unwrap(); - b.iter(|| {{ expr.search(&data).ok() }}); -}} - -"##, - test_fn_name, given_string, expr_string - ) - .as_bytes(), - ) - .expect("Error writing interpret benchmark"); - all_tests.push(test_fn_name) - } - - // Create the "full" benchmark if "full" - if bench_type == "full" { - let test_fn_name = format!("{}_full", fn_suffix); - f.write_all( - format!( - r##" - -fn {}(b: &mut Bencher) {{ - let data = Rcvar::new(Variable::from_json({:?}).expect("Invalid JSON given")); - b.iter(|| {{ compile({:?}).unwrap().search(&data).ok() }}); -}} - -"##, - test_fn_name, given_string, expr_string - ) - .as_bytes(), - ) - .expect("Error writing interpret benchmark"); - all_tests.push(test_fn_name) - } -} - -/// Generates a benchmark for a test case. -fn generate_test( - filename: &str, - suite_num: usize, - case_num: usize, - case: &serde_json::Map, - given_string: &str, - f: &mut File, -) { - let fn_suffix = generate_fn_name(filename, suite_num, case_num, case); - let case_string = serde_json::to_string(case).expect("Could not encode case"); - - f.write_all( - format!( - r##" -#[test] -fn test_{}() {{ - let case: TestCase = TestCase::from_str({:?}).unwrap(); - let data = Rcvar::new(Variable::from_json({:?}).unwrap()); - case.assert({:?}, data).unwrap(); -}} - -"##, - fn_suffix, case_string, given_string, filename - ) - .as_bytes(), - ) - .expect("Unable to write test"); -} diff --git a/lib/jmespath/jmespath/src/ast.rs b/lib/jmespath/jmespath/src/ast.rs deleted file mode 100644 index 94f434b..0000000 --- a/lib/jmespath/jmespath/src/ast.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! JMESPath abstract syntax tree (AST). -//! -//! Inspecting the JMESPath AST can be useful for analyzing the way in -//! which an expression was parsed and which features are utilized in -//! an expression. -//! -//! Ast can be accessed directly from a parsed `jmespath::Expression` -//! using the `as_ast()` method. An Ast can be created by using the -//! `jmespath::parse()` function which returns an Ast rather than an -//! `Expression`. -//! -//! ``` -//! use jmespath; -//! -//! let ast = jmespath::parse("a || b && c").unwrap(); -//! ``` - -use std::fmt; - -use crate::lexer::Token; -use crate::Rcvar; - -/// A JMESPath expression abstract syntax tree. -#[derive(Clone, PartialEq, Debug)] -pub enum Ast { - /// Compares two nodes using a comparator, returning true/false. - Comparison { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Comparator that compares the two results - comparator: Comparator, - /// Left hand side of the comparison - lhs: Box, - /// Right hand side of the comparison - rhs: Box, - }, - /// If `predicate` evaluates to a truthy value, returns the - /// result `then` - Condition { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// The predicate to test. - predicate: Box, - /// The node to traverse if the predicate is truthy. - then: Box, - }, - /// Returns the current node. - Identity { - /// Approximate absolute position in the parsed expression. - offset: usize, - }, - /// Used by functions to dynamically evaluate argument values. - Expref { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Node to execute - ast: Box, - }, - /// Evaluates the node, then flattens it one level. - Flatten { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Node to execute and flatten - node: Box, - }, - /// Function name and a vec or function argument expressions. - Function { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Function name to invoke. - name: String, - /// Function arguments. - args: Vec, - }, - /// Extracts a key by name from a map. - Field { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Field name to extract. - name: String, - }, - /// Extracts an index from a Vec. - Index { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Index to extract - idx: i32, - }, - /// Resolves to a literal value. - Literal { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Literal value - value: Rcvar, - }, - /// Evaluates to a list of evaluated expressions. - MultiList { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Elements of the list - elements: Vec, - }, - /// Evaluates to a map of key value pairs. - MultiHash { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Elements of the hash - elements: Vec, - }, - /// Evaluates to true/false based on if the expression is not truthy. - Not { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// node to negate - node: Box, - }, - /// Evaluates LHS, and pushes each value through RHS. - Projection { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Left hand side of the projection. - lhs: Box, - /// Right hand side of the projection. - rhs: Box, - }, - /// Evaluates LHS. If it resolves to an object, returns a Vec of values. - ObjectValues { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Node to extract object values from. - node: Box, - }, - /// Evaluates LHS. If not truthy returns. Otherwise evaluates RHS. - And { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Left hand side of the expression. - lhs: Box, - /// Right hand side of the expression. - rhs: Box, - }, - /// Evaluates LHS. If truthy returns. Otherwise evaluates RHS. - Or { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Left hand side of the expression. - lhs: Box, - /// Right hand side of the expression. - rhs: Box, - }, - /// Returns a slice of a vec, using start, stop, and step. - Slice { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Starting index - start: Option, - /// Stopping index - stop: Option, - /// Step amount between extractions. - step: i32, - }, - /// Evaluates RHS, then provides that value to the evaluation of RHS. - Subexpr { - /// Approximate absolute position in the parsed expression. - offset: usize, - /// Left hand side of the expression. - lhs: Box, - /// Right hand side of the expression. - rhs: Box, - }, -} - -impl fmt::Display for Ast { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(fmt, "{:#?}", self) - } -} - -/// Represents a key value pair in a MultiHash. -#[derive(Clone, PartialEq, Debug)] -pub struct KeyValuePair { - /// Key name. - pub key: String, - /// Value expression used to determine the value. - pub value: Ast, -} - -/// Comparators used in Comparison nodes. -#[derive(Clone, PartialEq, Debug)] -pub enum Comparator { - Equal, - NotEqual, - LessThan, - LessThanEqual, - GreaterThan, - GreaterThanEqual, -} - -/// Creates a Comparator from a Token. -/// -/// Note: panics if the Token is invalid. -impl From for Comparator { - fn from(token: Token) -> Self { - match token { - Token::Lt => Comparator::LessThan, - Token::Lte => Comparator::LessThanEqual, - Token::Gt => Comparator::GreaterThan, - Token::Gte => Comparator::GreaterThanEqual, - Token::Eq => Comparator::Equal, - Token::Ne => Comparator::NotEqual, - _ => panic!("Invalid token for comparator: {:?}", token), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn displays_pretty_printed_ast_node() { - let node = Ast::Field { - name: "abc".to_string(), - offset: 4, - }; - assert_eq!( - "Field {\n offset: 4,\n name: \"abc\",\n}", - format!("{}", node) - ); - } -} diff --git a/lib/jmespath/jmespath/src/errors.rs b/lib/jmespath/jmespath/src/errors.rs deleted file mode 100644 index dfb211c..0000000 --- a/lib/jmespath/jmespath/src/errors.rs +++ /dev/null @@ -1,319 +0,0 @@ -//! JMESPath errors. - -use std::error::Error; -use std::fmt; - -use crate::Context; - -/// JMESPath error. -#[derive(Clone, Debug, PartialEq)] -pub struct JmespathError { - /// Absolute character position. - pub offset: usize, - /// Line number of the coordinate. - pub line: usize, - /// Column of the line number. - pub column: usize, - /// Expression being evaluated. - pub expression: String, - /// Error reason information. - pub reason: ErrorReason, -} - -impl JmespathError { - /// Create a new JMESPath Error. - pub fn new(expr: &str, offset: usize, reason: ErrorReason) -> JmespathError { - // Find each new line so we can create a formatted error message. - let mut line: usize = 0; - let mut column: usize = 0; - for c in expr.chars().take(offset) { - match c { - '\n' => { - line += 1; - column = 0; - } - _ => column += 1, - } - } - JmespathError { - expression: expr.to_owned(), - offset, - line, - column, - reason, - } - } - - /// Create a new JMESPath Error from a Context struct. - pub fn from_ctx(ctx: &Context<'_>, reason: ErrorReason) -> JmespathError { - JmespathError::new(ctx.expression, ctx.offset, reason) - } -} - -impl Error for JmespathError { - fn description(&self) -> &str { - "error evaluating JMESPath expression" - } -} - -impl From for JmespathError { - fn from(err: serde_json::Error) -> Self { - JmespathError::new( - "", - 0, - ErrorReason::Parse(format!("Serde parse error: {}", err)), - ) - } -} - -fn inject_carat(column: usize, buff: &mut String) { - buff.push_str(&(0..column).map(|_| ' ').collect::()); - buff.push_str("^\n"); -} - -impl fmt::Display for JmespathError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let mut error_location = String::new(); - let mut matched = false; - let mut current_line = 0; - for c in self.expression.chars() { - error_location.push(c); - if c == '\n' { - current_line += 1; - if current_line == self.line + 1 { - matched = true; - inject_carat(self.column, &mut error_location); - } - } - } - if !matched { - error_location.push('\n'); - inject_carat(self.column, &mut error_location); - } - - write!( - fmt, - "{} (line {}, column {})\n{}", - self.reason, self.line, self.column, error_location - ) - } -} - -/// Error context to provide specific details about an error. -#[derive(Clone, Debug, PartialEq)] -pub enum ErrorReason { - /// An error occurred while parsing an expression. - Parse(String), - /// An error occurred while evaluating an expression. - Runtime(RuntimeError), -} - -impl fmt::Display for ErrorReason { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - ErrorReason::Parse(ref e) => write!(fmt, "Parse error: {}", e), - ErrorReason::Runtime(ref e) => write!(fmt, "Runtime error: {}", e), - } - } -} - -/// Runtime JMESPath error -#[derive(Clone, Debug, PartialEq)] -pub enum RuntimeError { - /// Encountered when a slice expression uses a step of 0 - InvalidSlice, - /// Encountered when too many arguments are provided to a function. - TooManyArguments { - /// Expeced number of arguments. - expected: usize, - /// Provided number of arguments. - actual: usize, - }, - /// Encountered when too few arguments are provided to a function. - NotEnoughArguments { - /// Expeced number of arguments. - expected: usize, - /// Provided number of arguments. - actual: usize, - }, - /// Encountered when an unknown function is called. - UnknownFunction(String), - /// Encountered when a type of variable given to a function is invalid. - InvalidType { - /// Expected type. - expected: String, - /// Provided type. - actual: String, - /// Argument position when calling the function. - position: usize, - }, - /// Encountered when an expression reference returns an invalid type. - InvalidReturnType { - /// Expected return type. - expected: String, - /// Actual return type. - actual: String, - /// Argument position from which the expression reference was invoked. - position: usize, - /// Which invocation iteration of the expression reference failed. - invocation: usize, - }, -} - -impl fmt::Display for RuntimeError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - use self::RuntimeError::*; - match *self { - UnknownFunction(ref function) => write!(fmt, "Call to undefined function {}", function), - TooManyArguments { - ref expected, - ref actual, - } => write!( - fmt, - "Too many arguments: expected {}, found {}", - expected, actual - ), - NotEnoughArguments { - ref expected, - ref actual, - } => write!( - fmt, - "Not enough arguments: expected {}, found {}", - expected, actual - ), - InvalidType { - ref expected, - ref actual, - ref position, - } => write!( - fmt, - "Argument {} expects type {}, given {}", - position, expected, actual - ), - InvalidSlice => write!(fmt, "Invalid slice"), - InvalidReturnType { - ref expected, - ref actual, - ref position, - ref invocation, - } => write!( - fmt, - "Argument {} must return {} but invocation {} returned {}", - position, expected, invocation, actual - ), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn coordinates_can_be_created_from_string_with_new_lines() { - let expr = "foo\n..bar"; - let err = JmespathError::new(&expr, 5, ErrorReason::Parse("Test".to_owned())); - assert_eq!(1, err.line); - assert_eq!(1, err.column); - assert_eq!(5, err.offset); - assert_eq!( - "Parse error: Test (line 1, column 1)\nfoo\n..bar\n ^\n", - err.to_string() - ); - } - - #[test] - fn coordinates_can_be_created_from_string_with_new_lines_pointing_to_non_last() { - let expr = "foo\n..bar\nbaz"; - let err = JmespathError::new(&expr, 5, ErrorReason::Parse("Test".to_owned())); - assert_eq!(1, err.line); - assert_eq!(1, err.column); - assert_eq!(5, err.offset); - assert_eq!( - "Parse error: Test (line 1, column 1)\nfoo\n..bar\n ^\nbaz", - err.to_string() - ); - } - - #[test] - fn coordinates_can_be_created_from_string_with_no_new_lines() { - let expr = "foo..bar"; - let err = JmespathError::new(&expr, 4, ErrorReason::Parse("Test".to_owned())); - assert_eq!(0, err.line); - assert_eq!(4, err.column); - assert_eq!(4, err.offset); - assert_eq!( - "Parse error: Test (line 0, column 4)\nfoo..bar\n ^\n", - err.to_string() - ); - } - - #[test] - fn reason_displays_parse_errors() { - let reason = ErrorReason::Parse("bar".to_owned()); - assert_eq!("Parse error: bar", reason.to_string()); - } - - #[test] - fn reason_displays_runtime_errors() { - let reason = ErrorReason::Runtime(RuntimeError::UnknownFunction("a".to_owned())); - assert_eq!( - "Runtime error: Call to undefined function a", - reason.to_string() - ); - } - - #[test] - fn displays_invalid_type_error() { - let error = RuntimeError::InvalidType { - expected: "string".to_owned(), - actual: "boolean".to_owned(), - position: 0, - }; - assert_eq!( - "Argument 0 expects type string, given boolean", - error.to_string() - ); - } - - #[test] - fn displays_invalid_slice() { - let error = RuntimeError::InvalidSlice; - assert_eq!("Invalid slice", error.to_string()); - } - - #[test] - fn displays_too_many_arguments_error() { - let error = RuntimeError::TooManyArguments { - expected: 1, - actual: 2, - }; - assert_eq!("Too many arguments: expected 1, found 2", error.to_string()); - } - - #[test] - fn displays_not_enough_arguments_error() { - let error = RuntimeError::NotEnoughArguments { - expected: 2, - actual: 1, - }; - assert_eq!( - "Not enough arguments: expected 2, found 1", - error.to_string() - ); - } - - #[test] - fn displays_invalid_return_type_error() { - let error = RuntimeError::InvalidReturnType { - expected: "string".to_string(), - actual: "boolean".to_string(), - position: 0, - invocation: 2, - }; - assert_eq!( - "Argument 0 must return string but invocation 2 returned boolean", - error.to_string() - ); - } -} diff --git a/lib/jmespath/jmespath/src/functions.rs b/lib/jmespath/jmespath/src/functions.rs deleted file mode 100644 index 1eb64d4..0000000 --- a/lib/jmespath/jmespath/src/functions.rs +++ /dev/null @@ -1,884 +0,0 @@ -//! JMESPath functions. - -use std::cmp::{max, min}; -use std::collections::BTreeMap; -use std::fmt; - -use crate::interpreter::{interpret, SearchResult}; -use crate::variable::{JmespathType, Variable}; -use crate::{Context, ErrorReason, JmespathError, Rcvar, RuntimeError}; -use serde_json::Number; - -/// Represents a JMESPath function. -pub trait Function: Sync { - /// Evaluates the function against an in-memory variable. - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult; -} - -/// Function argument types used when validating. -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum ArgumentType { - Any, - Null, - String, - Number, - Bool, - Object, - Array, - Expref, - /// Each element of the array must matched the provided type. - TypedArray(Box), - /// Accepts one of a number of `ArgumentType`s - Union(Vec), -} - -impl ArgumentType { - /// Returns true/false if the variable is valid for the type. - pub fn is_valid(&self, value: &Rcvar) -> bool { - use self::ArgumentType::*; - match *self { - Any => true, - Null if value.is_null() => true, - String if value.is_string() => true, - Number if value.is_number() => true, - Object if value.is_object() => true, - Bool if value.is_boolean() => true, - Expref if value.is_expref() => true, - Array if value.is_array() => true, - TypedArray(ref t) if value.is_array() => { - if let Some(array) = value.as_array() { - array.iter().all(|v| t.is_valid(v)) - } else { - false - } - } - Union(ref types) => types.iter().any(|t| t.is_valid(value)), - _ => false, - } - } -} - -impl fmt::Display for ArgumentType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - use self::ArgumentType::*; - match *self { - Any => write!(fmt, "any"), - String => write!(fmt, "string"), - Number => write!(fmt, "number"), - Bool => write!(fmt, "boolean"), - Array => write!(fmt, "array"), - Object => write!(fmt, "object"), - Null => write!(fmt, "null"), - Expref => write!(fmt, "expref"), - TypedArray(ref t) => write!(fmt, "array[{}]", t), - Union(ref types) => { - let str_value = types - .iter() - .map(|t| t.to_string()) - .collect::>() - .join("|"); - write!(fmt, "{}", str_value) - } - } - } -} - -macro_rules! arg { - (any) => (ArgumentType::Any); - (null) => (ArgumentType::Null); - (string) => (ArgumentType::String); - (bool) => (ArgumentType::Bool); - (number) => (ArgumentType::Number); - (object) => (ArgumentType::Object); - (expref) => (ArgumentType::Expref); - (array_number) => (ArgumentType::TypedArray(Box::new(ArgumentType::Number))); - (array_string) => (ArgumentType::TypedArray(Box::new(ArgumentType::String))); - (array) => (ArgumentType::Array); - ($($x:ident) | *) => (ArgumentType::Union(vec![$(arg!($x)), *])); -} - -/// Custom function that allows the creation of runtime functions with signature validation. -pub struct CustomFunction { - /// Signature used to validate the function. - signature: Signature, - /// Function to invoke after validating the signature. - f: Box) -> SearchResult + Sync>, -} - -impl CustomFunction { - /// Creates a new custom function. - pub fn new( - fn_signature: Signature, - f: Box) -> SearchResult + Sync>, - ) -> CustomFunction { - CustomFunction { - signature: fn_signature, - f, - } - } -} - -impl Function for CustomFunction { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - (self.f)(args, ctx) - } -} - -/// Normal closures can be used as functions. -/// -/// It is up to the function to validate the provided arguments. -/// If you wish to utilize Signatures or more complex argument -/// validation, it is recommended to use CustomFunction. -impl Function for F -where - F: Sync + Fn(&[Rcvar], &mut Context<'_>) -> SearchResult, -{ - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - (self)(args, ctx) - } -} - -/// Represents a function's signature. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct Signature { - pub inputs: Vec, - pub variadic: Option, -} - -impl Signature { - /// Creates a new Signature struct. - pub fn new(inputs: Vec, variadic: Option) -> Signature { - Signature { inputs, variadic } - } - - /// Validates the arity of a function. If the arity is invalid, a runtime - /// error is returned with the relative position of the error and the - /// expression that was being executed. - pub fn validate_arity(&self, actual: usize, ctx: &Context<'_>) -> Result<(), JmespathError> { - let expected = self.inputs.len(); - if self.variadic.is_some() { - if actual >= expected { - Ok(()) - } else { - let reason = - ErrorReason::Runtime(RuntimeError::NotEnoughArguments { expected, actual }); - Err(JmespathError::from_ctx(ctx, reason)) - } - } else if actual == expected { - Ok(()) - } else if actual < expected { - let reason = - ErrorReason::Runtime(RuntimeError::NotEnoughArguments { expected, actual }); - Err(JmespathError::from_ctx(ctx, reason)) - } else { - let reason = ErrorReason::Runtime(RuntimeError::TooManyArguments { expected, actual }); - Err(JmespathError::from_ctx(ctx, reason)) - } - } - - /// Validates the provided function arguments against the signature. - pub fn validate(&self, args: &[Rcvar], ctx: &Context<'_>) -> Result<(), JmespathError> { - self.validate_arity(args.len(), ctx)?; - if let Some(ref variadic) = self.variadic { - for (k, v) in args.iter().enumerate() { - let validator = self.inputs.get(k).unwrap_or(variadic); - self.validate_arg(ctx, k, v, validator)?; - } - } else { - for (k, v) in args.iter().enumerate() { - self.validate_arg(ctx, k, v, &self.inputs[k])?; - } - } - Ok(()) - } - - fn validate_arg( - &self, - ctx: &Context<'_>, - position: usize, - value: &Rcvar, - validator: &ArgumentType, - ) -> Result<(), JmespathError> { - if validator.is_valid(value) { - Ok(()) - } else { - let reason = ErrorReason::Runtime(RuntimeError::InvalidType { - expected: validator.to_string(), - actual: value.get_type().to_string(), - position, - }); - Err(JmespathError::from_ctx(ctx, reason)) - } - } -} - -/// Macro to more easily and quickly define a function and signature. -macro_rules! defn { - ($name:ident, $args:expr, $variadic:expr) => { - pub struct $name { - signature: Signature, - } - - impl Default for $name { - fn default() -> Self { - Self::new() - } - } - - impl $name { - pub fn new() -> $name { - $name { - signature: Signature::new($args, $variadic), - } - } - } - }; -} - -/// Macro used to implement max_by and min_by functions. -macro_rules! min_and_max_by { - ($ctx:expr, $operator:ident, $args:expr) => {{ - let vals = $args[0].as_array().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })?; - // Return null when there are not values in the array - if vals.is_empty() { - return Ok(Rcvar::new(Variable::Null)); - } - let ast = $args[1].as_expref().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be an expression".to_owned()), - ) - })?; - // Map over the first value to get the homogeneous required return type - let initial = interpret(&vals[0], &ast, $ctx)?; - let entered_type = initial.get_type(); - if entered_type != JmespathType::String && entered_type != JmespathType::Number { - return Err(JmespathError::from_ctx( - $ctx, - ErrorReason::Runtime(RuntimeError::InvalidReturnType { - expected: "expression->number|expression->string".to_owned(), - actual: entered_type.to_string(), - position: 1, - invocation: 1, - }), - )); - } - // Map over each value, finding the best candidate value and fail on error. - let mut candidate = (vals[0].clone(), initial.clone()); - for (invocation, v) in vals.iter().enumerate().skip(1) { - let mapped = interpret(v, &ast, $ctx)?; - if mapped.get_type() != entered_type { - return Err(JmespathError::from_ctx( - $ctx, - ErrorReason::Runtime(RuntimeError::InvalidReturnType { - expected: format!("expression->{}", entered_type), - actual: mapped.get_type().to_string(), - position: 1, - invocation, - }), - )); - } - if mapped.$operator(&candidate.1) { - candidate = (v.clone(), mapped); - } - } - Ok(candidate.0) - }}; -} - -/// Macro used to implement max and min functions. -macro_rules! min_and_max { - ($operator:ident, $args:expr) => {{ - let values = $args[0].as_array().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })?; - if values.is_empty() { - Ok(Rcvar::new(Variable::Null)) - } else { - let result: Rcvar = values - .iter() - .skip(1) - .fold(values[0].clone(), |acc, item| $operator(acc, item.clone())); - Ok(result) - } - }}; -} - -defn!(AbsFn, vec![arg!(number)], None); - -impl Function for AbsFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - match args[0].as_ref() { - Variable::Number(n) => Ok(Rcvar::new(Variable::Number( - Number::from_f64( - n.as_f64() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid f64".to_owned()), - ) - })? - .abs(), - ) - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid f64".to_owned()), - ) - })?, - ))), - _ => Ok(args[0].clone()), - } - } -} - -defn!(AvgFn, vec![arg!(array_number)], None); - -impl Function for AvgFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let values = args[0].as_array().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })?; - - let mut sum = 0.0; - - for value in values { - sum += value.as_number().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid f64".to_owned()), - ) - })?; - } - - Ok(Rcvar::new(Variable::Number( - Number::from_f64(sum / (values.len() as f64)).ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid f64".to_owned()), - ) - })?, - ))) - } -} - -defn!(CeilFn, vec![arg!(number)], None); - -impl Function for CeilFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let n = args[0].as_number().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a number".to_owned()), - ) - })?; - Ok(Rcvar::new(Variable::Number( - Number::from_f64(n.ceil()).ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected n.ceil() to be a valid f64".to_owned()), - ) - })?, - ))) - } -} - -defn!(ContainsFn, vec![arg!(string | array), arg!(any)], None); - -impl Function for ContainsFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let haystack = &args[0]; - let needle = &args[1]; - match **haystack { - Variable::Array(ref a) => Ok(Rcvar::new(Variable::Bool(a.contains(needle)))), - Variable::String(ref subj) => match needle.as_string() { - None => Ok(Rcvar::new(Variable::Bool(false))), - Some(s) => Ok(Rcvar::new(Variable::Bool(subj.contains(s)))), - }, - _ => unreachable!(), - } - } -} - -defn!(EndsWithFn, vec![arg!(string), arg!(string)], None); - -impl Function for EndsWithFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let subject = args[0].as_string().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a valid string".to_owned()), - ) - })?; - let search = args[1].as_string().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be a valid string".to_owned()), - ) - })?; - Ok(Rcvar::new(Variable::Bool(subject.ends_with(search)))) - } -} - -defn!(FloorFn, vec![arg!(number)], None); - -impl Function for FloorFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let n = args[0].as_number().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a valid number".to_owned()), - ) - })?; - Ok(Rcvar::new(Variable::Number( - Number::from_f64(n.floor()).ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid number".to_owned()), - ) - })?, - ))) - } -} - -defn!(JoinFn, vec![arg!(string), arg!(array_string)], None); - -impl Function for JoinFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let glue = args[0].as_string().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a valid string".to_owned()), - ) - })?; - let values = args[1].as_array().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be a valid string".to_owned()), - ) - })?; - let result = values - .iter() - .map(|v| { - v.as_string().map(|val| val.to_owned()).ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid string".to_owned()), - ) - }) - }) - .collect::, JmespathError>>()? - .join(glue); - Ok(Rcvar::new(Variable::String(result))) - } -} - -defn!(KeysFn, vec![arg!(object)], None); - -impl Function for KeysFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let object = args[0].as_object().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a valid Object".to_owned()), - ) - })?; - let keys = object - .keys() - .map(|k| Rcvar::new(Variable::String((*k).clone()))) - .collect::>(); - Ok(Rcvar::new(Variable::Array(keys))) - } -} - -defn!(LengthFn, vec![arg!(array | object | string)], None); - -impl Function for LengthFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - match args[0].as_ref() { - Variable::Array(ref a) => Ok(Rcvar::new(Variable::Number(Number::from(a.len())))), - Variable::Object(ref m) => Ok(Rcvar::new(Variable::Number(Number::from(m.len())))), - // Note that we need to count the code points not the number of unicode characters - Variable::String(ref s) => Ok(Rcvar::new(Variable::Number(Number::from( - s.chars().count(), - )))), - _ => unreachable!(), - } - } -} - -defn!(MapFn, vec![arg!(expref), arg!(array)], None); - -impl Function for MapFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let ast = args[0].as_expref().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an expref".to_owned()), - ) - })?; - let values = args[1].as_array().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be an array".to_owned()), - ) - })?; - let mut results = vec![]; - for value in values { - results.push(interpret(value, ast, ctx)?); - } - Ok(Rcvar::new(Variable::Array(results))) - } -} - -defn!(MaxFn, vec![arg!(array_string | array_number)], None); - -impl Function for MaxFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - min_and_max!(max, args) - } -} - -defn!(MinFn, vec![arg!(array_string | array_number)], None); - -impl Function for MinFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - min_and_max!(min, args) - } -} - -defn!(MaxByFn, vec![arg!(array), arg!(expref)], None); - -impl Function for MaxByFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - min_and_max_by!(ctx, gt, args) - } -} - -defn!(MinByFn, vec![arg!(array), arg!(expref)], None); - -impl Function for MinByFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - min_and_max_by!(ctx, lt, args) - } -} - -defn!(MergeFn, vec![arg!(object)], Some(arg!(object))); - -impl Function for MergeFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let mut result = BTreeMap::new(); - for arg in args { - result.extend( - arg.as_object() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid Object".to_owned()), - ) - })? - .clone(), - ); - } - Ok(Rcvar::new(Variable::Object(result))) - } -} - -defn!(NotNullFn, vec![arg!(any)], Some(arg!(any))); - -impl Function for NotNullFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - for arg in args { - if !arg.is_null() { - return Ok(arg.clone()); - } - } - Ok(Rcvar::new(Variable::Null)) - } -} - -defn!(ReverseFn, vec![arg!(array | string)], None); - -impl Function for ReverseFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - if args[0].is_array() { - let mut values = args[0] - .as_array() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })? - .clone(); - values.reverse(); - Ok(Rcvar::new(Variable::Array(values))) - } else { - let word: String = args[0] - .as_string() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a string".to_owned()), - ) - })? - .chars() - .rev() - .collect(); - Ok(Rcvar::new(Variable::String(word))) - } - } -} - -defn!(SortFn, vec![arg!(array_string | array_number)], None); - -impl Function for SortFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let mut values = args[0] - .as_array() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })? - .clone(); - values.sort(); - Ok(Rcvar::new(Variable::Array(values))) - } -} - -defn!(SortByFn, vec![arg!(array), arg!(expref)], None); - -impl Function for SortByFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let vals = args[0] - .as_array() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })? - .clone(); - if vals.is_empty() { - return Ok(Rcvar::new(Variable::Array(vals))); - } - let ast = args[1].as_expref().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be an expref".to_owned()), - ) - })?; - let mut mapped: Vec<(Rcvar, Rcvar)> = vec![]; - let first_value = interpret(&vals[0], ast, ctx)?; - let first_type = first_value.get_type(); - if first_type != JmespathType::String && first_type != JmespathType::Number { - let reason = ErrorReason::Runtime(RuntimeError::InvalidReturnType { - expected: "expression->string|expression->number".to_owned(), - actual: first_type.to_string(), - position: 1, - invocation: 1, - }); - return Err(JmespathError::from_ctx(ctx, reason)); - } - mapped.push((vals[0].clone(), first_value)); - for (invocation, v) in vals.iter().enumerate().skip(1) { - let mapped_value = interpret(v, ast, ctx)?; - if mapped_value.get_type() != first_type { - return Err(JmespathError::from_ctx( - ctx, - ErrorReason::Runtime(RuntimeError::InvalidReturnType { - expected: format!("expression->{}", first_type), - actual: mapped_value.get_type().to_string(), - position: 1, - invocation, - }), - )); - } - mapped.push((v.clone(), mapped_value)); - } - mapped.sort_by(|a, b| a.1.cmp(&b.1)); - let result = mapped.iter().map(|tuple| tuple.0.clone()).collect(); - Ok(Rcvar::new(Variable::Array(result))) - } -} - -defn!(StartsWithFn, vec![arg!(string), arg!(string)], None); - -impl Function for StartsWithFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let subject = args[0].as_string().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be a string".to_owned()), - ) - })?; - let search = args[1].as_string().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be a string".to_owned()), - ) - })?; - Ok(Rcvar::new(Variable::Bool(subject.starts_with(search)))) - } -} - -defn!(SumFn, vec![arg!(array_number)], None); - -impl Function for SumFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let result = args[0] - .as_array() - .ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[0] to be an array".to_owned()), - ) - })? - .iter() - .fold(0.0, |acc, item| acc + item.as_number().unwrap_or(0.0)); - Ok(Rcvar::new(Variable::Number( - Number::from_f64(result).ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected to be a valid number".to_owned()), - ) - })?, - ))) - } -} - -defn!(ToArrayFn, vec![arg!(any)], None); - -impl Function for ToArrayFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - match *args[0] { - Variable::Array(_) => Ok(args[0].clone()), - _ => Ok(Rcvar::new(Variable::Array(vec![args[0].clone()]))), - } - } -} - -defn!(ToNumberFn, vec![arg!(any)], None); - -impl Function for ToNumberFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - match *args[0] { - Variable::Number(_) => Ok(args[0].clone()), - Variable::String(ref s) => match Variable::from_json(s) { - Ok(f) => Ok(Rcvar::new(f)), - Err(_) => Ok(Rcvar::new(Variable::Null)), - }, - _ => Ok(Rcvar::new(Variable::Null)), - } - } -} - -defn!( - ToStringFn, - vec![arg!(object | array | bool | number | string | null)], - None -); - -impl Function for ToStringFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - match *args[0] { - Variable::String(_) => Ok(args[0].clone()), - _ => Ok(Rcvar::new(Variable::String(args[0].to_string()))), - } - } -} - -defn!(TypeFn, vec![arg!(any)], None); - -impl Function for TypeFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - Ok(Rcvar::new(Variable::String(args[0].get_type().to_string()))) - } -} - -defn!(ValuesFn, vec![arg!(object)], None); - -impl Function for ValuesFn { - fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult { - self.signature.validate(args, ctx)?; - let map = args[0].as_object().ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse("Expected args[1] to be an Object".to_owned()), - ) - })?; - Ok(Rcvar::new(Variable::Array( - map.values().cloned().collect::>(), - ))) - } -} diff --git a/lib/jmespath/jmespath/src/interpreter.rs b/lib/jmespath/jmespath/src/interpreter.rs deleted file mode 100644 index 97a9060..0000000 --- a/lib/jmespath/jmespath/src/interpreter.rs +++ /dev/null @@ -1,185 +0,0 @@ -//! Interprets JMESPath expressions. - -use std::collections::BTreeMap; - -use super::ast::Ast; -use super::variable::Variable; -use super::Context; -use super::{ErrorReason, JmespathError, Rcvar, RuntimeError}; - -/// Result of searching data using a JMESPath Expression. -pub type SearchResult = Result; - -/// Interprets the given data using an AST node. -pub fn interpret(data: &Rcvar, node: &Ast, ctx: &mut Context<'_>) -> SearchResult { - match *node { - Ast::Field { ref name, .. } => Ok(data.get_field(name)), - Ast::Subexpr { - ref lhs, ref rhs, .. - } => { - let left_result = interpret(data, lhs, ctx)?; - interpret(&left_result, rhs, ctx) - } - Ast::Identity { .. } => Ok(data.clone()), - Ast::Literal { ref value, .. } => Ok(value.clone()), - Ast::Index { idx, .. } => { - if idx >= 0 { - Ok(data.get_index(idx as usize)) - } else { - Ok(data.get_negative_index((-idx) as usize)) - } - } - Ast::Or { - ref lhs, ref rhs, .. - } => { - let left = interpret(data, lhs, ctx)?; - if left.is_truthy() { - Ok(left) - } else { - interpret(data, rhs, ctx) - } - } - Ast::And { - ref lhs, ref rhs, .. - } => { - let left = interpret(data, lhs, ctx)?; - if !left.is_truthy() { - Ok(left) - } else { - interpret(data, rhs, ctx) - } - } - Ast::Not { ref node, .. } => { - let result = interpret(data, node, ctx)?; - Ok(Rcvar::new(Variable::Bool(!result.is_truthy()))) - } - // Returns the resut of RHS if cond yields truthy value. - Ast::Condition { - ref predicate, - ref then, - .. - } => { - let cond_result = interpret(data, predicate, ctx)?; - if cond_result.is_truthy() { - interpret(data, then, ctx) - } else { - Ok(Rcvar::new(Variable::Null)) - } - } - Ast::Comparison { - ref comparator, - ref lhs, - ref rhs, - .. - } => { - let left = interpret(data, lhs, ctx)?; - let right = interpret(data, rhs, ctx)?; - Ok(left - .compare(comparator, &*right) - .map_or(Rcvar::new(Variable::Null), |result| { - Rcvar::new(Variable::Bool(result)) - })) - } - // Converts an object into a JSON array of its values. - Ast::ObjectValues { ref node, .. } => { - let subject = interpret(data, node, ctx)?; - match *subject { - Variable::Object(ref v) => Ok(Rcvar::new(Variable::Array( - v.values().cloned().collect::>(), - ))), - _ => Ok(Rcvar::new(Variable::Null)), - } - } - // Passes the results of lhs into rhs if lhs yields an array and - // each node of lhs that passes through rhs yields a non-null value. - Ast::Projection { - ref lhs, ref rhs, .. - } => match interpret(data, lhs, ctx)?.as_array() { - None => Ok(Rcvar::new(Variable::Null)), - Some(left) => { - let mut collected = vec![]; - for element in left { - let current = interpret(element, rhs, ctx)?; - if !current.is_null() { - collected.push(current); - } - } - Ok(Rcvar::new(Variable::Array(collected))) - } - }, - Ast::Flatten { ref node, .. } => match interpret(data, node, ctx)?.as_array() { - None => Ok(Rcvar::new(Variable::Null)), - Some(a) => { - let mut collected: Vec = vec![]; - for element in a { - match element.as_array() { - Some(array) => collected.extend(array.iter().cloned()), - _ => collected.push(element.clone()), - } - } - Ok(Rcvar::new(Variable::Array(collected))) - } - }, - Ast::MultiList { ref elements, .. } => { - if data.is_null() { - Ok(Rcvar::new(Variable::Null)) - } else { - let mut collected = vec![]; - for node in elements { - collected.push(interpret(data, node, ctx)?); - } - Ok(Rcvar::new(Variable::Array(collected))) - } - } - Ast::MultiHash { ref elements, .. } => { - if data.is_null() { - Ok(Rcvar::new(Variable::Null)) - } else { - let mut collected = BTreeMap::new(); - for kvp in elements { - let value = interpret(data, &kvp.value, ctx)?; - collected.insert(kvp.key.clone(), value); - } - Ok(Rcvar::new(Variable::Object(collected))) - } - } - Ast::Function { - ref name, - ref args, - offset, - } => { - let mut fn_args: Vec = vec![]; - for arg in args { - fn_args.push(interpret(data, arg, ctx)?); - } - // Reset the offset so that it points to the function being evaluated. - ctx.offset = offset; - match ctx.runtime.get_function(name) { - Some(f) => f.evaluate(&fn_args, ctx), - None => { - let reason = - ErrorReason::Runtime(RuntimeError::UnknownFunction(name.to_owned())); - Err(JmespathError::from_ctx(ctx, reason)) - } - } - } - Ast::Expref { ref ast, .. } => Ok(Rcvar::new(Variable::Expref(*ast.clone()))), - Ast::Slice { - start, - stop, - step, - offset, - } => { - if step == 0 { - ctx.offset = offset; - let reason = ErrorReason::Runtime(RuntimeError::InvalidSlice); - Err(JmespathError::from_ctx(ctx, reason)) - } else { - match data.slice(start, stop, step) { - Some(array) => Ok(Rcvar::new(Variable::Array(array))), - None => Ok(Rcvar::new(Variable::Null)), - } - } - } - } -} diff --git a/lib/jmespath/jmespath/src/lexer.rs b/lib/jmespath/jmespath/src/lexer.rs deleted file mode 100644 index 27d029a..0000000 --- a/lib/jmespath/jmespath/src/lexer.rs +++ /dev/null @@ -1,537 +0,0 @@ -//! Module for tokenizing JMESPath expressions. -//! -//! The lexer returns a VecDeque of tuples where each tuple contains the -//! character position in the original string from which the lexeme originates -//! followed by the token itself. The VecDeque is then consumed by the parser. -//! A VecDeque is utilized in order to pop owned tokens and provide arbitrary -//! token lookahead in the parser. - -use std::collections::VecDeque; -use std::iter::Peekable; -use std::str::CharIndices; - -use self::Token::*; -use crate::variable::Variable; -use crate::{ErrorReason, JmespathError, Rcvar}; - -/// Represents a lexical token of a JMESPath expression. -#[derive(Clone, PartialEq, Debug)] -pub enum Token { - Identifier(String), - QuotedIdentifier(String), - Number(i32), - Literal(Rcvar), - Dot, - Star, - Flatten, - And, - Or, - Pipe, - Filter, - Lbracket, - Rbracket, - Comma, - Colon, - Not, - Ne, - Eq, - Gt, - Gte, - Lt, - Lte, - At, - Ampersand, - Lparen, - Rparen, - Lbrace, - Rbrace, - Eof, -} - -impl Token { - /// Provides the left binding power of the token. - /// - /// This is used in the parser to determine whether or not - /// the currently parsing expression should continue parsing - /// by consuming a token. - #[inline] - pub fn lbp(&self) -> usize { - match *self { - Pipe => 1, - Or => 2, - And => 3, - Eq => 5, - Gt => 5, - Lt => 5, - Gte => 5, - Lte => 5, - Ne => 5, - Flatten => 9, - Star => 20, - Filter => 21, - Dot => 40, - Not => 45, - Lbrace => 50, - Lbracket => 55, - Lparen => 60, - _ => 0, - } - } -} - -/// A tuple of the token position and the token. -pub type TokenTuple = (usize, Token); - -/// Tokenizes a JMESPath expression. -pub fn tokenize(expr: &str) -> Result, JmespathError> { - Lexer::new(expr).tokenize() -} - -struct Lexer<'a> { - iter: Peekable>, - expr: &'a str, -} - -impl<'a> Lexer<'a> { - fn new(expr: &'a str) -> Lexer<'a> { - Lexer { - iter: expr.char_indices().peekable(), - expr, - } - } - - fn tokenize(&mut self) -> Result, JmespathError> { - let mut tokens = VecDeque::new(); - let last_position = self.expr.len(); - loop { - match self.iter.next() { - Some((pos, ch)) => { - match ch { - 'a'..='z' | 'A'..='Z' | '_' => { - tokens.push_back((pos, self.consume_identifier(ch))) - } - '.' => tokens.push_back((pos, Dot)), - '[' => tokens.push_back((pos, self.consume_lbracket())), - '*' => tokens.push_back((pos, Star)), - '|' => tokens.push_back((pos, self.alt('|', Or, Pipe))), - '@' => tokens.push_back((pos, At)), - ']' => tokens.push_back((pos, Rbracket)), - '{' => tokens.push_back((pos, Lbrace)), - '}' => tokens.push_back((pos, Rbrace)), - '&' => tokens.push_back((pos, self.alt('&', And, Ampersand))), - '(' => tokens.push_back((pos, Lparen)), - ')' => tokens.push_back((pos, Rparen)), - ',' => tokens.push_back((pos, Comma)), - ':' => tokens.push_back((pos, Colon)), - '"' => tokens.push_back((pos, self.consume_quoted_identifier(pos)?)), - '\'' => tokens.push_back((pos, self.consume_raw_string(pos)?)), - '`' => tokens.push_back((pos, self.consume_literal(pos)?)), - '=' => match self.iter.next() { - Some((_, c)) if c == '=' => tokens.push_back((pos, Eq)), - _ => { - let message = "'=' is not valid. Did you mean '=='?"; - let reason = ErrorReason::Parse(message.to_owned()); - return Err(JmespathError::new(self.expr, pos, reason)); - } - }, - '>' => tokens.push_back((pos, self.alt('=', Gte, Gt))), - '<' => tokens.push_back((pos, self.alt('=', Lte, Lt))), - '!' => tokens.push_back((pos, self.alt('=', Ne, Not))), - '0'..='9' => tokens.push_back((pos, self.consume_number(ch, false))), - '-' => tokens.push_back((pos, self.consume_negative_number(pos)?)), - // Skip whitespace tokens - ' ' | '\n' | '\t' | '\r' => {} - c => { - let reason = ErrorReason::Parse(format!("Invalid character: {}", c)); - return Err(JmespathError::new(self.expr, pos, reason)); - } - } - } - None => { - tokens.push_back((last_position, Eof)); - return Ok(tokens); - } - } - } - } - - // Consumes characters while the predicate function returns true. - #[inline] - fn consume_while(&mut self, mut buffer: String, predicate: F) -> String - where - F: Fn(char) -> bool, - { - loop { - match self.iter.peek() { - None => break, - Some(&(_, c)) if !predicate(c) => break, - Some(&(_, c)) => { - buffer.push(c); - self.iter.next(); - } - } - } - buffer - } - - // Consumes "[", "[]", "[? - #[inline] - fn consume_lbracket(&mut self) -> Token { - match self.iter.peek() { - Some(&(_, ']')) => { - self.iter.next(); - Flatten - } - Some(&(_, '?')) => { - self.iter.next(); - Filter - } - _ => Lbracket, - } - } - - // Consume identifiers: ( ALPHA / "_" ) *( DIGIT / ALPHA / "_" ) - #[inline] - fn consume_identifier(&mut self, first_char: char) -> Token { - Identifier(self.consume_while( - first_char.to_string(), - |c| matches!(c, 'a'..='z' | '_' | 'A'..='Z' | '0'..='9'), - )) - } - - // Consumes numbers: *"-" "0" / ( %x31-39 *DIGIT ) - #[inline] - fn consume_number(&mut self, first_char: char, is_negative: bool) -> Token { - let lexeme = self.consume_while(first_char.to_string(), |c| c.is_digit(10)); - let numeric_value: i32 = lexeme.parse().expect("Expected valid number"); - if is_negative { - Number(-numeric_value) - } else { - Number(numeric_value) - } - } - - // Consumes a negative number - #[inline] - fn consume_negative_number(&mut self, pos: usize) -> Result { - // Ensure that the next value is a number > 0 - match self.iter.next() { - Some((_, c)) if c.is_numeric() && c != '0' => Ok(self.consume_number(c, true)), - _ => { - let reason = ErrorReason::Parse("'-' must be followed by numbers 1-9".to_owned()); - Err(JmespathError::new(self.expr, pos, reason)) - } - } - } - - // Consumes tokens inside of a closing character. The closing character - // can be escaped using a "\" character. - #[inline] - fn consume_inside( - &mut self, - pos: usize, - wrapper: char, - invoke: F, - ) -> Result - where - F: Fn(String) -> Result, - { - let mut buffer = String::new(); - while let Some((_, c)) = self.iter.next() { - if c == wrapper { - return invoke(buffer) - .map_err(|e| JmespathError::new(self.expr, pos, ErrorReason::Parse(e))); - } else if c == '\\' { - buffer.push(c); - if let Some((_, c)) = self.iter.next() { - buffer.push(c); - } - } else { - buffer.push(c) - } - } - // The token was not closed, so error with the string, including the - // wrapper (e.g., '"foo'). - let message = format!("Unclosed {} delimiter: {}{}", wrapper, wrapper, buffer); - Err(JmespathError::new( - self.expr, - pos, - ErrorReason::Parse(message), - )) - } - - // Consume and parse a quoted identifier token. - #[inline] - fn consume_quoted_identifier(&mut self, pos: usize) -> Result { - self.consume_inside(pos, '"', |s| { - // JSON decode the string to expand escapes - match Variable::from_json(format!(r##""{}""##, s).as_ref()) { - // Convert the JSON value into a string literal. - Ok(j) => Ok(QuotedIdentifier(j.as_string().cloned().ok_or_else( - || "consume_quoted_identifier expected a string".to_owned(), - )?)), - Err(e) => Err(format!("Unable to parse quoted identifier {}: {}", s, e)), - } - }) - } - - #[inline] - fn consume_raw_string(&mut self, pos: usize) -> Result { - // Note: we need to unescape here because the backslashes are passed through. - self.consume_inside(pos, '\'', |s| { - Ok(Literal(Rcvar::new(Variable::String(s.replace("\\'", "'"))))) - }) - } - - // Consume and parse a literal JSON token. - #[inline] - fn consume_literal(&mut self, pos: usize) -> Result { - self.consume_inside(pos, '`', |s| { - let unescaped = s.replace("\\`", "`"); - match Variable::from_json(unescaped.as_ref()) { - Ok(j) => Ok(Literal(Rcvar::new(j))), - Err(err) => Err(format!("Unable to parse literal JSON {}: {}", s, err)), - } - }) - } - - #[inline] - fn alt(&mut self, expected: char, match_type: Token, else_type: Token) -> Token { - match self.iter.peek() { - Some(&(_, c)) if c == expected => { - self.iter.next(); - match_type - } - _ => else_type, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::variable::Variable; - use crate::Rcvar; - - fn tokenize_queue(expr: &str) -> Vec { - let mut result = tokenize(expr).unwrap(); - let mut v = Vec::new(); - while let Some(node) = result.pop_front() { - v.push(node); - } - v - } - - #[test] - fn tokenize_basic_test() { - assert_eq!(tokenize_queue("."), vec![(0, Dot), (1, Eof)]); - assert_eq!(tokenize_queue("*"), vec![(0, Star), (1, Eof)]); - assert_eq!(tokenize_queue("@"), vec![(0, At), (1, Eof)]); - assert_eq!(tokenize_queue("]"), vec![(0, Rbracket), (1, Eof)]); - assert_eq!(tokenize_queue("{"), vec![(0, Lbrace), (1, Eof)]); - assert_eq!(tokenize_queue("}"), vec![(0, Rbrace), (1, Eof)]); - assert_eq!(tokenize_queue("("), vec![(0, Lparen), (1, Eof)]); - assert_eq!(tokenize_queue(")"), vec![(0, Rparen), (1, Eof)]); - assert_eq!(tokenize_queue(","), vec![(0, Comma), (1, Eof)]); - } - - #[test] - fn tokenize_lbracket_test() { - assert_eq!(tokenize_queue("["), vec![(0, Lbracket), (1, Eof)]); - assert_eq!(tokenize_queue("[]"), vec![(0, Flatten), (2, Eof)]); - assert_eq!(tokenize_queue("[?"), vec![(0, Filter), (2, Eof)]); - } - - #[test] - fn tokenize_pipe_test() { - assert_eq!(tokenize_queue("|"), vec![(0, Pipe), (1, Eof)]); - assert_eq!(tokenize_queue("||"), vec![(0, Or), (2, Eof)]); - } - - #[test] - fn tokenize_and_ampersand_test() { - assert_eq!(tokenize_queue("&"), vec![(0, Ampersand), (1, Eof)]); - assert_eq!(tokenize_queue("&&"), vec![(0, And), (2, Eof)]); - } - - #[test] - fn tokenize_lt_gt_test() { - assert_eq!(tokenize_queue("<"), vec![(0, Lt), (1, Eof)]); - assert_eq!(tokenize_queue("<="), vec![(0, Lte), (2, Eof)]); - assert_eq!(tokenize_queue(">"), vec![(0, Gt), (1, Eof)]); - assert_eq!(tokenize_queue(">="), vec![(0, Gte), (2, Eof)]); - } - - #[test] - fn tokenize_eq_ne_test() { - assert_eq!(tokenize_queue("=="), vec![(0, Eq), (2, Eof)]); - assert_eq!(tokenize_queue("!"), vec![(0, Not), (1, Eof)]); - assert_eq!(tokenize_queue("!="), vec![(0, Ne), (2, Eof)]); - } - - #[test] - fn ensures_eq_valid() { - assert!(tokenize("=").is_err()); - } - - #[test] - fn skips_whitespace() { - let tokens = tokenize_queue(" \t\n\r\t. ("); - assert_eq!(tokens, vec![(5, Dot), (7, Lparen), (8, Eof)]); - } - - #[test] - fn tokenize_single_error_test() { - assert!(tokenize("~") - .unwrap_err() - .to_string() - .contains("Invalid character: ~")); - } - - #[test] - fn tokenize_unclosed_errors_test() { - assert!(tokenize("\"foo") - .unwrap_err() - .to_string() - .contains("Unclosed \" delimiter: \"foo")); - assert!(tokenize("`foo") - .unwrap_err() - .to_string() - .contains("Unclosed ` delimiter: `foo")); - } - - #[test] - fn tokenize_identifier_test() { - assert_eq!( - tokenize_queue("foo_bar"), - vec![(0, Identifier("foo_bar".to_string())), (7, Eof)] - ); - assert_eq!( - tokenize_queue("a"), - vec![(0, Identifier("a".to_string())), (1, Eof)] - ); - assert_eq!( - tokenize_queue("_a"), - vec![(0, Identifier("_a".to_string())), (2, Eof)] - ); - } - - #[test] - fn tokenize_quoted_identifier_test() { - assert_eq!( - tokenize_queue("\"foo\""), - vec![(0, QuotedIdentifier("foo".to_string())), (5, Eof)] - ); - assert_eq!( - tokenize_queue("\"\""), - vec![(0, QuotedIdentifier("".to_string())), (2, Eof)] - ); - assert_eq!( - tokenize_queue("\"a_b\""), - vec![(0, QuotedIdentifier("a_b".to_string())), (5, Eof)] - ); - assert_eq!( - tokenize_queue("\"a\\nb\""), - vec![(0, QuotedIdentifier("a\nb".to_string())), (6, Eof)] - ); - assert_eq!( - tokenize_queue("\"a\\\\nb\""), - vec![(0, QuotedIdentifier("a\\nb".to_string())), (7, Eof)] - ); - } - - #[test] - fn tokenize_raw_string_test() { - assert_eq!( - tokenize_queue("'foo'"), - vec![ - (0, Literal(Rcvar::new(Variable::String("foo".to_string())))), - (5, Eof) - ] - ); - assert_eq!( - tokenize_queue("''"), - vec![ - (0, Literal(Rcvar::new(Variable::String("".to_string())))), - (2, Eof) - ] - ); - assert_eq!( - tokenize_queue("'a\\nb'"), - vec![ - ( - 0, - Literal(Rcvar::new(Variable::String("a\\nb".to_string()))) - ), - (6, Eof) - ] - ); - } - - #[test] - fn tokenize_literal_test() { - // Must enclose in quotes. See JEP 12. - assert!(tokenize("`a`") - .unwrap_err() - .to_string() - .contains("Unable to parse")); - assert_eq!( - tokenize_queue("`\"a\"`"), - vec![ - (0, Literal(Rcvar::new(Variable::String("a".to_string())))), - (5, Eof) - ] - ); - assert_eq!( - tokenize_queue("`\"a b\"`"), - vec![ - (0, Literal(Rcvar::new(Variable::String("a b".to_string())))), - (7, Eof) - ] - ); - } - - #[test] - fn tokenize_number_test() { - assert_eq!(tokenize_queue("0"), vec![(0, Number(0)), (1, Eof)]); - assert_eq!(tokenize_queue("1"), vec![(0, Number(1)), (1, Eof)]); - assert_eq!(tokenize_queue("123"), vec![(0, Number(123)), (3, Eof)]); - } - - #[test] - fn tokenize_negative_number_test() { - assert_eq!(tokenize_queue("-10"), vec![(0, Number(-10)), (3, Eof)]); - } - - #[test] - fn tokenize_negative_number_test_failure() { - assert!(tokenize("-01").unwrap_err().to_string().contains("'-'")); - } - - #[test] - fn tokenize_successive_test() { - let expr = "foo.bar || `\"a\"` | 10"; - let tokens = tokenize_queue(expr); - assert_eq!(tokens[0], (0, Identifier("foo".to_string()))); - assert_eq!(tokens[1], (3, Dot)); - assert_eq!(tokens[2], (4, Identifier("bar".to_string()))); - assert_eq!(tokens[3], (8, Or)); - assert_eq!( - tokens[4], - (11, Literal(Rcvar::new(Variable::String("a".to_string())))) - ); - assert_eq!(tokens[5], (17, Pipe)); - assert_eq!(tokens[6], (19, Number(10))); - assert_eq!(tokens[7], (21, Eof)); - } - - #[test] - fn tokenizes_slices() { - let tokens = tokenize_queue("foo[0::-1]"); - assert_eq!( - "[(0, Identifier(\"foo\")), (3, Lbracket), (4, Number(0)), (5, Colon), \ - (6, Colon), (7, Number(-1)), (9, Rbracket), (10, Eof)]", - format!("{:?}", tokens) - ); - } -} diff --git a/lib/jmespath/jmespath/src/lib.rs b/lib/jmespath/jmespath/src/lib.rs deleted file mode 100644 index 4c67c05..0000000 --- a/lib/jmespath/jmespath/src/lib.rs +++ /dev/null @@ -1,511 +0,0 @@ -//! Rust implementation of JMESPath, a query language for JSON. -//! -//! # Compiling JMESPath expressions -//! -//! Use the `jmespath::compile` function to compile JMESPath expressions -//! into reusable `Expression` structs. The `Expression` struct can be -//! used multiple times on different values without having to recompile -//! the expression. -//! -//! ``` -//! use jmespath; -//! -//! let expr = jmespath::compile("foo.bar | baz").unwrap(); -//! -//! // Parse some JSON data into a JMESPath variable -//! let json_str = "{\"foo\":{\"bar\":{\"baz\":true}}}"; -//! let data = jmespath::Variable::from_json(json_str).unwrap(); -//! -//! // Search the data with the compiled expression -//! let result = expr.search(data).unwrap(); -//! assert_eq!(true, result.as_boolean().unwrap()); -//! ``` -//! -//! You can get the original expression as a string and the parsed expression -//! AST from the `Expression` struct: -//! -//! ``` -//! use jmespath; -//! use jmespath::ast::Ast; -//! -//! let expr = jmespath::compile("foo").unwrap(); -//! assert_eq!("foo", expr.as_str()); -//! assert_eq!(&Ast::Field {name: "foo".to_string(), offset: 0}, expr.as_ast()); -//! ``` -//! -//! ## JMESPath variables -//! -//! In order to evaluate expressions against a known data type, the -//! `jmespath::Variable` enum is used as both the input and output type. -//! More specifically, `Rcvar` (or `jmespath::Rcvar`) is used to allow -//! shared, reference counted data to be used by the JMESPath interpreter at -//! runtime. -//! -//! By default, `Rcvar` is an `std::rc::Rc`. However, by specifying -//! the `sync` feature, you can utilize an `std::sync::Arc` to -//! share `Expression` structs across threads. -//! -//! Any type that implements `jmespath::ToJmespath` can be used in a JMESPath -//! Expression. Various types have default `ToJmespath` implementations, -//! including `serde::ser::Serialize`. Because `jmespath::Variable` implements -//! `serde::ser::Serialize`, many existing types can be searched without needing -//! an explicit coercion, and any type that needs coercion can be implemented -//! using serde's macros or code generation capabilities. This includes a -//! number of common types, including serde's `serde_json::Value` enum. -//! -//! The return value of searching data with JMESPath is also an `Rcvar`. -//! `Variable` has a number of helper methods that make it a data type that -//! can be used directly, or you can convert `Variable` to any serde value -//! implementing `serde::de::Deserialize`. -//! -//! # Custom Functions -//! -//! You can register custom functions with a JMESPath expression by using -//! a custom `Runtime`. When you call `jmespath::compile`, you are using a -//! shared `Runtime` instance that is created lazily using `lazy_static`. -//! This shared `Runtime` utilizes all of the builtin JMESPath functions -//! by default. However, custom functions may be utilized by creating a custom -//! `Runtime` and compiling expressions directly from the `Runtime`. -//! -//! ``` -//! use jmespath::{Runtime, Context, Rcvar}; -//! use jmespath::functions::{CustomFunction, Signature, ArgumentType}; -//! -//! // Create a new Runtime and register the builtin JMESPath functions. -//! let mut runtime = Runtime::new(); -//! runtime.register_builtin_functions(); -//! -//! // Create an identity string function that returns string values as-is. -//! runtime.register_function("str_identity", Box::new(CustomFunction::new( -//! Signature::new(vec![ArgumentType::String], None), -//! Box::new(|args: &[Rcvar], _: &mut Context| Ok(args[0].clone())) -//! ))); -//! -//! // You can also use normal closures as functions. -//! runtime.register_function("identity", -//! Box::new(|args: &[Rcvar], _: &mut Context| Ok(args[0].clone()))); -//! -//! let expr = runtime.compile("str_identity('foo')").unwrap(); -//! assert_eq!("foo", expr.search(()).unwrap().as_string().unwrap()); -//! -//! let expr = runtime.compile("identity('bar')").unwrap(); -//! assert_eq!("bar", expr.search(()).unwrap().as_string().unwrap()); -//! ``` - -#![cfg_attr(feature = "specialized", feature(specialization))] - -pub use crate::errors::{ErrorReason, JmespathError, RuntimeError}; -pub use crate::parser::{parse, ParseResult}; -pub use crate::runtime::Runtime; -pub use crate::variable::Variable; - -pub mod ast; -pub mod functions; - -use serde::ser; -#[cfg(feature = "specialized")] -use serde_json::Value; -#[cfg(feature = "specialized")] -use std::convert::TryInto; -use std::fmt; - -use lazy_static::*; - -use crate::ast::Ast; -use crate::interpreter::{interpret, SearchResult}; - -mod errors; -mod interpreter; -mod lexer; -mod parser; -mod runtime; -mod variable; - -lazy_static! { - pub static ref DEFAULT_RUNTIME: Runtime = { - let mut runtime = Runtime::new(); - runtime.register_builtin_functions(); - runtime - }; -} - -/// `Rc` reference counted JMESPath `Variable`. -#[cfg(not(feature = "sync"))] -pub type Rcvar = std::rc::Rc; -/// `Arc` reference counted JMESPath `Variable`. -#[cfg(feature = "sync")] -pub type Rcvar = std::sync::Arc; - -/// Compiles a JMESPath expression using the default Runtime. -/// -/// The default Runtime is created lazily the first time it is dereferenced -/// by using the `lazy_static` macro. -/// -/// The provided expression is expected to adhere to the JMESPath -/// grammar: -#[inline] -pub fn compile(expression: &str) -> Result, JmespathError> { - DEFAULT_RUNTIME.compile(expression) -} - -/// Converts a value into a reference-counted JMESPath Variable. -/// -#[cfg_attr( - feature = "specialized", - doc = "\ -There is a generic serde Serialize implementation, and since this -documentation was compiled with the `specialized` feature turned -**on**, there are also a number of specialized implementations for -`ToJmespath` built into the library that should work for most -cases." -)] -#[cfg_attr( - not(feature = "specialized"), - doc = "\ -There is a generic serde Serialize implementation. Since this -documentation was compiled with the `specialized` feature turned -**off**, this is the only implementation available. - -(If the `specialized` feature were turned on, there there would be -a number of additional specialized implementations for `ToJmespath` -built into the library that should work for most cases.)" -)] -pub trait ToJmespath { - fn to_jmespath(self) -> Result; -} - -/// Create searchable values from Serde serializable values. -impl<'a, T: ser::Serialize> ToJmespath for T { - #[cfg(not(feature = "specialized"))] - fn to_jmespath(self) -> Result { - Variable::from_serializable(self).map(Rcvar::new) - } - - #[cfg(feature = "specialized")] - default fn to_jmespath(self) -> Result { - Variable::from_serializable(self).map(|var| Rcvar::new(var)) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for Value { - #[inline] - fn to_jmespath(self) -> Result { - self.try_into().map(|var: Variable| Rcvar::new(var)) - } -} - -#[cfg(feature = "specialized")] -impl<'a> ToJmespath for &'a Value { - #[inline] - fn to_jmespath(self) -> Result { - self.try_into().map(|var: Variable| Rcvar::new(var)) - } -} - -#[cfg(feature = "specialized")] -/// Identity coercion. -impl ToJmespath for Rcvar { - #[inline] - fn to_jmespath(self) -> Result { - Ok(self) - } -} - -#[cfg(feature = "specialized")] -impl<'a> ToJmespath for &'a Rcvar { - #[inline] - fn to_jmespath(self) -> Result { - Ok(self.clone()) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for Variable { - #[inline] - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(self)) - } -} - -#[cfg(feature = "specialized")] -impl<'a> ToJmespath for &'a Variable { - #[inline] - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(self.clone())) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for String { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::String(self))) - } -} - -#[cfg(feature = "specialized")] -impl<'a> ToJmespath for &'a str { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::String(self.to_owned()))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for i8 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for i16 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for i32 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for i64 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for u8 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for u16 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for u32 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for u64 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for isize { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for usize { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self)))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for f32 { - fn to_jmespath(self) -> Result { - (self as f64).to_jmespath() - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for f64 { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Number( - serde_json::Number::from_f64(self).ok_or_else(|| { - JmespathError::new( - "", - 0, - ErrorReason::Parse(format!("Cannot parse {} into a Number", self)), - ) - })?, - ))) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for () { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Null)) - } -} - -#[cfg(feature = "specialized")] -impl ToJmespath for bool { - fn to_jmespath(self) -> Result { - Ok(Rcvar::new(Variable::Bool(self))) - } -} - -/// A compiled JMESPath expression. -/// -/// The compiled expression can be used multiple times without incurring -/// the cost of re-parsing the expression each time. The expression may -/// be shared between threads if JMESPath is compiled with the `sync` -/// feature, which forces the use of an `Arc` instead of an `Rc` for -/// runtime variables. -#[derive(Clone)] -pub struct Expression<'a> { - ast: Ast, - expression: String, - runtime: &'a Runtime, -} - -impl<'a> Expression<'a> { - /// Creates a new JMESPath expression. - /// - /// Normally you will create expressions using either `jmespath::compile()` - /// or using a jmespath::Runtime. - #[inline] - pub fn new(expression: S, ast: Ast, runtime: &'a Runtime) -> Expression<'a> - where - S: Into, - { - Expression { - expression: expression.into(), - ast, - runtime, - } - } - - /// Returns the result of searching data with the compiled expression. - /// - /// The SearchResult contains a JMESPath Rcvar, or a reference counted - /// Variable. This value can be used directly like a JSON object. - /// Alternatively, Variable does implement Serde serialzation and - /// deserialization, so it can easily be marshalled to another type. - pub fn search(&self, data: T) -> SearchResult { - let mut ctx = Context::new(&self.expression, self.runtime); - interpret(&data.to_jmespath()?, &self.ast, &mut ctx) - } - - /// Returns the JMESPath expression from which the Expression was compiled. - /// - /// Note that this is the same value that is returned by calling - /// `to_string`. - pub fn as_str(&self) -> &str { - &self.expression - } - - /// Returns the AST of the parsed JMESPath expression. - /// - /// This can be useful for debugging purposes, caching, etc. - pub fn as_ast(&self) -> &Ast { - &self.ast - } -} - -impl<'a> fmt::Display for Expression<'a> { - /// Shows the jmespath expression as a string. - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl<'a> fmt::Debug for Expression<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl<'a> PartialEq for Expression<'a> { - fn eq(&self, other: &Expression<'_>) -> bool { - self.as_str() == other.as_str() - } -} - -/// Context object used for error reporting. -/// -/// The Context struct is mostly used when interacting between the -/// interpreter and function implemenations. Unless you're writing custom -/// JMESPath functions, this struct is an implementation detail. -pub struct Context<'a> { - /// Expression string that is being interpreted. - pub expression: &'a str, - /// JMESPath runtime used to compile the expression and call functions. - pub runtime: &'a Runtime, - /// Ast offset that is currently being evaluated. - pub offset: usize, -} - -impl<'a> Context<'a> { - /// Create a new context struct. - #[inline] - pub fn new(expression: &'a str, runtime: &'a Runtime) -> Context<'a> { - Context { - expression, - runtime, - offset: 0, - } - } -} - -#[cfg(test)] -mod test { - use super::ast::Ast; - use super::*; - - #[test] - fn formats_expression_as_string_or_debug() { - let expr = compile("foo | baz").unwrap(); - assert_eq!("foo | baz/foo | baz", format!("{}/{:?}", expr, expr)); - } - - #[test] - fn implements_partial_eq() { - let a = compile("@").unwrap(); - let b = compile("@").unwrap(); - assert!(a == b); - } - - #[test] - fn can_evaluate_jmespath_expression() { - let expr = compile("foo.bar").unwrap(); - let var = Variable::from_json("{\"foo\":{\"bar\":true}}").unwrap(); - assert_eq!(Rcvar::new(Variable::Bool(true)), expr.search(var).unwrap()); - } - - #[test] - fn can_get_expression_ast() { - let expr = compile("foo").unwrap(); - assert_eq!( - &Ast::Field { - offset: 0, - name: "foo".to_string(), - }, - expr.as_ast() - ); - } - - #[test] - fn test_creates_rcvar_from_tuple_serialization() { - use super::ToJmespath; - let t = (true, false); - assert_eq!("[true,false]", t.to_jmespath().unwrap().to_string()); - } - - #[test] - fn expression_clone() { - let expr = compile("foo").unwrap(); - let _ = expr.clone(); - } -} diff --git a/lib/jmespath/jmespath/src/parser.rs b/lib/jmespath/jmespath/src/parser.rs deleted file mode 100644 index b1cd001..0000000 --- a/lib/jmespath/jmespath/src/parser.rs +++ /dev/null @@ -1,473 +0,0 @@ -//! Module for parsing JMESPath expressions into an AST. -//! -//! This JMESPath parser is implemented using a Pratt parser, -//! or top down operator precedence parser: -//! - -use std::collections::VecDeque; - -use crate::ast::{Ast, Comparator, KeyValuePair}; -use crate::lexer::{tokenize, Token, TokenTuple}; -use crate::{ErrorReason, JmespathError}; - -/// Result of parsing an expression. -pub type ParseResult = Result; - -/// Parses a JMESPath expression into an AST. -pub fn parse(expr: &str) -> ParseResult { - let tokens = tokenize(expr)?; - Parser::new(tokens, expr).parse() -} - -/// The maximum binding power for a token that can stop a projection. -const PROJECTION_STOP: usize = 10; - -struct Parser<'a> { - /// Parsed tokens - token_queue: VecDeque, - /// Shared EOF token - eof_token: Token, - /// Expression being parsed - expr: &'a str, - /// The current character offset in the expression - offset: usize, -} - -impl<'a> Parser<'a> { - fn new(tokens: VecDeque, expr: &'a str) -> Parser<'a> { - Parser { - token_queue: tokens, - eof_token: Token::Eof, - offset: 0, - expr, - } - } - - #[inline] - fn parse(&mut self) -> ParseResult { - self.expr(0).and_then(|result| { - // After parsing the expr, we should reach the end of the stream. - match self.peek(0) { - &Token::Eof => Ok(result), - t => Err(self.err(t, "Did not parse the complete expression", true)), - } - }) - } - - #[inline] - fn advance(&mut self) -> Token { - self.advance_with_pos().1 - } - - #[inline] - fn advance_with_pos(&mut self) -> (usize, Token) { - match self.token_queue.pop_front() { - Some((pos, tok)) => { - self.offset = pos; - (pos, tok) - } - None => (self.offset, Token::Eof), - } - } - - #[inline] - fn peek(&self, lookahead: usize) -> &Token { - match self.token_queue.get(lookahead) { - Some(&(_, ref t)) => t, - None => &self.eof_token, - } - } - - /// Returns a formatted error with the given message. - fn err(&self, current_token: &Token, error_msg: &str, is_peek: bool) -> JmespathError { - let mut actual_pos = self.offset; - let mut buff = error_msg.to_string(); - buff.push_str(&format!(" -- found {:?}", current_token)); - if is_peek { - if let Some(&(p, _)) = self.token_queue.get(0) { - actual_pos = p; - } - } - JmespathError::new(self.expr, actual_pos, ErrorReason::Parse(buff)) - } - - /// Main parse function of the Pratt parser that parses while RBP < LBP - fn expr(&mut self, rbp: usize) -> ParseResult { - let mut left = self.nud(); - while rbp < self.peek(0).lbp() { - left = self.led(Box::new(left?)); - } - left - } - - fn nud(&mut self) -> ParseResult { - let (offset, token) = self.advance_with_pos(); - match token { - Token::At => Ok(Ast::Identity { offset }), - Token::Identifier(value) => Ok(Ast::Field { - name: value, - offset, - }), - Token::QuotedIdentifier(value) => match self.peek(0) { - Token::Lparen => { - let message = "Quoted strings can't be a function name"; - Err(self.err(&Token::Lparen, message, true)) - } - _ => Ok(Ast::Field { - name: value, - offset, - }), - }, - Token::Star => self.parse_wildcard_values(Box::new(Ast::Identity { offset })), - Token::Literal(value) => Ok(Ast::Literal { value, offset }), - Token::Lbracket => match self.peek(0) { - &Token::Number(_) | &Token::Colon => self.parse_index(), - &Token::Star if self.peek(1) == &Token::Rbracket => { - self.advance(); - self.parse_wildcard_index(Box::new(Ast::Identity { offset })) - } - _ => self.parse_multi_list(), - }, - Token::Flatten => self.parse_flatten(Box::new(Ast::Identity { offset })), - Token::Lbrace => { - let mut pairs = vec![]; - loop { - // Requires at least on key value pair. - pairs.push(self.parse_kvp()?); - match self.advance() { - // Terminal condition is the Rbrace token - Token::Rbrace => break, - // Skip commas as they are used to delineate kvps - Token::Comma => continue, - ref t => return Err(self.err(t, "Expected '}' or ','", false)), - } - } - Ok(Ast::MultiHash { - elements: pairs, - offset, - }) - } - t @ Token::Ampersand => { - let rhs = self.expr(t.lbp())?; - Ok(Ast::Expref { - ast: Box::new(rhs), - offset, - }) - } - t @ Token::Not => Ok(Ast::Not { - node: Box::new(self.expr(t.lbp())?), - offset, - }), - Token::Filter => self.parse_filter(Box::new(Ast::Identity { offset })), - Token::Lparen => { - let result = self.expr(0)?; - match self.advance() { - Token::Rparen => Ok(result), - ref t => Err(self.err(t, "Expected ')' to close '('", false)), - } - } - ref t => Err(self.err(t, "Unexpected nud token", false)), - } - } - - fn led(&mut self, left: Box) -> ParseResult { - let (offset, token) = self.advance_with_pos(); - match token { - t @ Token::Dot => { - if self.peek(0) == &Token::Star { - // Skip the star and parse the rhs - self.advance(); - self.parse_wildcard_values(left) - } else { - let offset = offset; - let rhs = self.parse_dot(t.lbp())?; - Ok(Ast::Subexpr { - offset, - lhs: left, - rhs: Box::new(rhs), - }) - } - } - Token::Lbracket => { - if match self.peek(0) { - &Token::Number(_) | &Token::Colon => true, - &Token::Star => false, - t => return Err(self.err(t, "Expected number, ':', or '*'", true)), - } { - Ok(Ast::Subexpr { - offset, - lhs: left, - rhs: Box::new(self.parse_index()?), - }) - } else { - self.advance(); - self.parse_wildcard_index(left) - } - } - t @ Token::Or => { - let offset = offset; - let rhs = self.expr(t.lbp())?; - Ok(Ast::Or { - offset, - lhs: left, - rhs: Box::new(rhs), - }) - } - t @ Token::And => { - let offset = offset; - let rhs = self.expr(t.lbp())?; - Ok(Ast::And { - offset, - lhs: left, - rhs: Box::new(rhs), - }) - } - t @ Token::Pipe => { - let offset = offset; - let rhs = self.expr(t.lbp())?; - Ok(Ast::Subexpr { - offset, - lhs: left, - rhs: Box::new(rhs), - }) - } - Token::Lparen => match *left { - Ast::Field { name: v, .. } => Ok(Ast::Function { - offset, - name: v, - args: self.parse_list(Token::Rparen)?, - }), - _ => Err(self.err(self.peek(0), "Invalid function name", true)), - }, - Token::Flatten => self.parse_flatten(left), - Token::Filter => self.parse_filter(left), - Token::Eq => self.parse_comparator(Comparator::Equal, left), - Token::Ne => self.parse_comparator(Comparator::NotEqual, left), - Token::Gt => self.parse_comparator(Comparator::GreaterThan, left), - Token::Gte => self.parse_comparator(Comparator::GreaterThanEqual, left), - Token::Lt => self.parse_comparator(Comparator::LessThan, left), - Token::Lte => self.parse_comparator(Comparator::LessThanEqual, left), - ref t => Err(self.err(t, "Unexpected led token", false)), - } - } - - fn parse_kvp(&mut self) -> Result { - match self.advance() { - Token::Identifier(value) | Token::QuotedIdentifier(value) => { - if self.peek(0) == &Token::Colon { - self.advance(); - Ok(KeyValuePair { - key: value, - value: self.expr(0)?, - }) - } else { - Err(self.err(self.peek(0), "Expected ':' to follow key", true)) - } - } - ref t => Err(self.err(t, "Expected Field to start key value pair", false)), - } - } - - /// Parses a filter token into a Projection that filters the right - /// side of the projection using a Condition node. If the Condition node - /// returns a truthy value, then the value is yielded by the projection. - fn parse_filter(&mut self, lhs: Box) -> ParseResult { - // Parse the LHS of the condition node. - let condition_lhs = Box::new(self.expr(0)?); - // Eat the closing bracket. - match self.advance() { - Token::Rbracket => { - let condition_rhs = Box::new(self.projection_rhs(Token::Filter.lbp())?); - Ok(Ast::Projection { - offset: self.offset, - lhs, - rhs: Box::new(Ast::Condition { - offset: self.offset, - predicate: condition_lhs, - then: condition_rhs, - }), - }) - } - ref t => Err(self.err(t, "Expected ']'", false)), - } - } - - fn parse_flatten(&mut self, lhs: Box) -> ParseResult { - let rhs = Box::new(self.projection_rhs(Token::Flatten.lbp())?); - Ok(Ast::Projection { - offset: self.offset, - lhs: Box::new(Ast::Flatten { - offset: self.offset, - node: lhs, - }), - rhs, - }) - } - - /// Parses a comparator token into a Comparison (e.g., foo == bar) - fn parse_comparator(&mut self, cmp: Comparator, lhs: Box) -> ParseResult { - let rhs = Box::new(self.expr(Token::Eq.lbp())?); - Ok(Ast::Comparison { - offset: self.offset, - comparator: cmp, - lhs, - rhs, - }) - } - - /// Parses the right hand side of a dot expression. - fn parse_dot(&mut self, lbp: usize) -> ParseResult { - if match self.peek(0) { - &Token::Lbracket => true, - &Token::Identifier(_) - | &Token::QuotedIdentifier(_) - | &Token::Star - | &Token::Lbrace - | &Token::Ampersand => false, - t => return Err(self.err(t, "Expected identifier, '*', '{', '[', '&', or '[?'", true)), - } { - self.advance(); - self.parse_multi_list() - } else { - self.expr(lbp) - } - } - - /// Parses the right hand side of a projection, using the given LBP to - /// determine when to stop consuming tokens. - fn projection_rhs(&mut self, lbp: usize) -> ParseResult { - if match self.peek(0) { - &Token::Dot => true, - &Token::Lbracket | &Token::Filter => false, - t if t.lbp() < PROJECTION_STOP => { - return Ok(Ast::Identity { - offset: self.offset, - }); - } - t => { - return Err(self.err(t, "Expected '.', '[', or '[?'", true)); - } - } { - self.advance(); - self.parse_dot(lbp) - } else { - self.expr(lbp) - } - } - - /// Creates a projection for "[*]" - fn parse_wildcard_index(&mut self, lhs: Box) -> ParseResult { - match self.advance() { - Token::Rbracket => { - let rhs = Box::new(self.projection_rhs(Token::Star.lbp())?); - Ok(Ast::Projection { - offset: self.offset, - lhs, - rhs, - }) - } - ref t => Err(self.err(t, "Expected ']' for wildcard index", false)), - } - } - - /// Creates a projection for "*" - fn parse_wildcard_values(&mut self, lhs: Box) -> ParseResult { - let rhs = Box::new(self.projection_rhs(Token::Star.lbp())?); - Ok(Ast::Projection { - offset: self.offset, - lhs: Box::new(Ast::ObjectValues { - offset: self.offset, - node: lhs, - }), - rhs, - }) - } - - /// Parses [0], [::-1], [0:-1], [0:1], etc... - fn parse_index(&mut self) -> ParseResult { - let mut parts = [None, None, None]; - let mut pos = 0; - loop { - match self.advance() { - Token::Number(value) => { - parts[pos] = Some(value); - match self.peek(0) { - &Token::Colon | &Token::Rbracket => (), - t => return Err(self.err(t, "Expected ':', or ']'", true)), - }; - } - Token::Rbracket => break, - Token::Colon if pos >= 2 => { - return Err(self.err(&Token::Colon, "Too many colons in slice expr", false)); - } - Token::Colon => { - pos += 1; - match self.peek(0) { - &Token::Number(_) | &Token::Colon | &Token::Rbracket => continue, - t => return Err(self.err(t, "Expected number, ':', or ']'", true)), - }; - } - ref t => return Err(self.err(t, "Expected number, ':', or ']'", false)), - } - } - - if pos == 0 { - // No colons were found, so this is a simple index extraction. - Ok(Ast::Index { - offset: self.offset, - idx: parts[0].ok_or_else(|| { - JmespathError::new( - self.expr, - self.offset, - ErrorReason::Parse( - "Expected parts[0] to be Some; but found None".to_owned(), - ), - ) - })?, - }) - } else { - // Sliced array from start (e.g., [2:]) - Ok(Ast::Projection { - offset: self.offset, - lhs: Box::new(Ast::Slice { - offset: self.offset, - start: parts[0], - stop: parts[1], - step: parts[2].unwrap_or(1), - }), - rhs: Box::new(self.projection_rhs(Token::Star.lbp())?), - }) - } - } - - /// Parses multi-select lists (e.g., "[foo, bar, baz]") - fn parse_multi_list(&mut self) -> ParseResult { - Ok(Ast::MultiList { - offset: self.offset, - elements: self.parse_list(Token::Rbracket)?, - }) - } - - /// Parse a comma separated list of expressions until a closing token. - /// - /// This function is used for functions and multi-list parsing. Note - /// that this function allows empty lists. This is fine when parsing - /// multi-list expressions because "[]" is tokenized as Token::Flatten. - /// - /// Examples: [foo, bar], foo(bar), foo(), foo(baz, bar) - fn parse_list(&mut self, closing: Token) -> Result, JmespathError> { - let mut nodes = vec![]; - while self.peek(0) != &closing { - nodes.push(self.expr(0)?); - // Skip commas - if self.peek(0) == &Token::Comma { - self.advance(); - if self.peek(0) == &closing { - return Err(self.err(self.peek(0), "invalid token after ','", true)); - } - } - } - self.advance(); - Ok(nodes) - } -} diff --git a/lib/jmespath/jmespath/src/runtime.rs b/lib/jmespath/jmespath/src/runtime.rs deleted file mode 100644 index 68295b5..0000000 --- a/lib/jmespath/jmespath/src/runtime.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::collections::HashMap; - -use crate::functions::*; -use crate::parse; -use crate::Expression; -use crate::JmespathError; - -/// Compiles JMESPath expressions. -/// -/// Most use cases don't need to worry about how Runtime works. -/// You really only need to create your own Runtimes if you are -/// utilizing custom functions in your expressions. -pub struct Runtime { - functions: HashMap>, -} - -impl Default for Runtime { - fn default() -> Self { - Runtime { - functions: HashMap::with_capacity(26), - } - } -} - -impl Runtime { - /// Creates a new Runtime. - pub fn new() -> Runtime { - Default::default() - } - - /// Creates a new JMESPath expression from an expression string. - /// - /// The provided expression is expected to adhere to the JMESPath - /// grammar: - #[inline] - pub fn compile<'a>(&'a self, expression: &str) -> Result, JmespathError> { - parse(expression).map(|ast| Expression::new(expression, ast, self)) - } - - /// Adds a new function to the runtime. - #[inline] - pub fn register_function(&mut self, name: &str, f: Box) { - self.functions.insert(name.to_owned(), f); - } - - /// Removes a function from the runtime. - /// - /// Returns the function that was removed if it was found. - pub fn deregister_function(&mut self, name: &str) -> Option> { - self.functions.remove(name) - } - - /// Gets a function by name from the runtime. - #[inline] - pub fn get_function<'a>(&'a self, name: &str) -> Option<&'a dyn Function> { - self.functions.get(name).map(AsRef::as_ref) - } - - /// Registers all of the builtin JMESPath functions with the runtime. - pub fn register_builtin_functions(&mut self) { - self.register_function("abs", Box::new(AbsFn::new())); - self.register_function("avg", Box::new(AvgFn::new())); - self.register_function("ceil", Box::new(CeilFn::new())); - self.register_function("contains", Box::new(ContainsFn::new())); - self.register_function("ends_with", Box::new(EndsWithFn::new())); - self.register_function("floor", Box::new(FloorFn::new())); - self.register_function("join", Box::new(JoinFn::new())); - self.register_function("keys", Box::new(KeysFn::new())); - self.register_function("length", Box::new(LengthFn::new())); - self.register_function("map", Box::new(MapFn::new())); - self.register_function("min", Box::new(MinFn::new())); - self.register_function("max", Box::new(MaxFn::new())); - self.register_function("max_by", Box::new(MaxByFn::new())); - self.register_function("min_by", Box::new(MinByFn::new())); - self.register_function("merge", Box::new(MergeFn::new())); - self.register_function("not_null", Box::new(NotNullFn::new())); - self.register_function("reverse", Box::new(ReverseFn::new())); - self.register_function("sort", Box::new(SortFn::new())); - self.register_function("sort_by", Box::new(SortByFn::new())); - self.register_function("starts_with", Box::new(StartsWithFn::new())); - self.register_function("sum", Box::new(SumFn::new())); - self.register_function("to_array", Box::new(ToArrayFn::new())); - self.register_function("to_number", Box::new(ToNumberFn::new())); - self.register_function("to_string", Box::new(ToStringFn::new())); - self.register_function("type", Box::new(TypeFn::new())); - self.register_function("values", Box::new(ValuesFn::new())); - } -} diff --git a/lib/jmespath/jmespath/src/variable.rs b/lib/jmespath/jmespath/src/variable.rs deleted file mode 100644 index 8df8748..0000000 --- a/lib/jmespath/jmespath/src/variable.rs +++ /dev/null @@ -1,1654 +0,0 @@ -//! Module for JMESPath runtime variables. - -use serde::de::IntoDeserializer; -use serde::*; -use serde_json::error::Error; -use serde_json::value::Value; -use std::cmp::{max, Ordering}; -use std::collections::BTreeMap; -use std::fmt; -use std::iter::Iterator; -use std::string::ToString; -use std::vec; - -use crate::ast::{Ast, Comparator}; -use crate::ToJmespath; -use crate::{JmespathError, Rcvar}; -use serde_json::Number; -use std::convert::TryFrom; - -/// JMESPath types. -#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)] -pub enum JmespathType { - Null, - String, - Number, - Boolean, - Array, - Object, - Expref, -} - -impl fmt::Display for JmespathType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - JmespathType::Null => "null", - JmespathType::String => "string", - JmespathType::Number => "number", - JmespathType::Boolean => "boolean", - JmespathType::Array => "array", - JmespathType::Object => "object", - JmespathType::Expref => "expref", - } - ) - } -} - -/// JMESPath variable. -#[derive(Clone, Debug)] -pub enum Variable { - Null, - String(String), - Bool(bool), - Number(Number), - Array(Vec), - Object(BTreeMap), - Expref(Ast), -} - -impl Eq for Variable {} - -/// Compares two floats for equality. -/// -/// Allows for equivalence of floating point numbers like -/// 0.7100000000000002 and 0.71. -/// -/// Based on http://stackoverflow.com/a/4915891 -fn float_eq(a: f64, b: f64) -> bool { - use std::f64; - let abs_a = a.abs(); - let abs_b = b.abs(); - let diff = (a - b).abs(); - if a == b { - true - } else if !a.is_normal() || !b.is_normal() { - // a or b is zero or both are extremely close to it - // relative error is less meaningful here. - diff < (f64::EPSILON * f64::MIN_POSITIVE) - } else { - // use relative error. - diff / (abs_a + abs_b) < f64::EPSILON - } -} - -/// Implement PartialEq for looser floating point comparisons. -impl PartialEq for Variable { - fn eq(&self, other: &Variable) -> bool { - if self.get_type() != other.get_type() { - false - } else { - match self { - Variable::Number(a) => { - if let (Some(a), Some(b)) = (a.as_f64(), other.as_number()) { - float_eq(a, b) - } else { - false - } - } - Variable::String(ref s) => Some(s) == other.as_string(), - Variable::Bool(b) => Some(*b) == other.as_boolean(), - Variable::Array(ref a) => Some(a) == other.as_array(), - Variable::Object(ref o) => Some(o) == other.as_object(), - Variable::Expref(ref e) => Some(e) == other.as_expref(), - Variable::Null => true, - } - } - } -} - -/// Implement PartialOrd so that Ast can be in the PartialOrd of Variable. -impl PartialOrd for Variable { - fn partial_cmp(&self, other: &Variable) -> Option { - Some(self.cmp(other)) - } - - fn lt(&self, other: &Variable) -> bool { - self.cmp(other) == Ordering::Less - } - - fn le(&self, other: &Variable) -> bool { - let ordering = self.cmp(other); - ordering == Ordering::Equal || ordering == Ordering::Less - } - - fn gt(&self, other: &Variable) -> bool { - self.cmp(other) == Ordering::Greater - } - - fn ge(&self, other: &Variable) -> bool { - let ordering = self.cmp(other); - ordering == Ordering::Equal || ordering == Ordering::Greater - } -} - -impl Ord for Variable { - fn cmp(&self, other: &Self) -> Ordering { - let var_type = self.get_type(); - // Variables of different types are considered equal. - if var_type != other.get_type() { - Ordering::Equal - } else { - match var_type { - JmespathType::String => { - if let (Some(a), Some(b)) = (self.as_string(), other.as_string()) { - a.cmp(b) - } else { - Ordering::Equal - } - } - JmespathType::Number => { - if let (Some(a), Some(b)) = (self.as_number(), other.as_number()) { - a.partial_cmp(&b).unwrap_or(Ordering::Less) - } else { - Ordering::Equal - } - } - _ => Ordering::Equal, - } - } - } -} - -/// Write the JSON representation of a value, converting expref to a JSON -/// string containing the debug dump of the expref variable. -impl fmt::Display for Variable { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - serde_json::to_string(self) - .unwrap_or_else(|err| format!("unable to stringify Variable. Err: {}", err)) - ) - } -} - -/// Generic way of converting a Map in a Value to a Variable. -fn convert_map<'a, T>(value: T) -> Result -where - T: Iterator, -{ - let mut map: BTreeMap = BTreeMap::new(); - for kvp in value { - map.insert(kvp.0.to_owned(), kvp.1.to_jmespath()?); - } - Ok(Variable::Object(map)) -} - -/// Convert a borrowed Value to a Variable. -impl<'a> TryFrom<&'a Value> for Variable { - type Error = JmespathError; - - fn try_from(value: &'a Value) -> Result { - let var = match *value { - Value::String(ref s) => Variable::String(s.to_owned()), - Value::Null => Variable::Null, - Value::Bool(b) => Variable::Bool(b), - Value::Number(ref n) => Variable::Number(n.clone()), - Value::Object(ref values) => convert_map(values.iter())?, - Value::Array(ref values) => Variable::Array( - values - .iter() - .map(|v| v.to_jmespath()) - .collect::>()?, - ), - }; - Ok(var) - } -} - -/// Slightly optimized method for converting from an owned Value. -impl TryFrom for Variable { - type Error = JmespathError; - - fn try_from(value: Value) -> Result { - let var = match value { - Value::String(s) => Variable::String(s), - Value::Null => Variable::Null, - Value::Bool(b) => Variable::Bool(b), - Value::Number(n) => Variable::Number(n), - Value::Object(ref values) => convert_map(values.iter())?, - Value::Array(mut values) => Variable::Array( - values - .drain(..) - .map(|v| v.to_jmespath()) - .collect::>()?, - ), - }; - Ok(var) - } -} - -impl Variable { - /// Shortcut function to encode a `T` into a JMESPath `Variable` - pub fn from_serializable(value: T) -> Result - where - T: ser::Serialize, - { - Ok(to_variable(value)?) - } - - /// Create a JMESPath Variable from a JSON encoded string. - pub fn from_json(s: &str) -> Result { - serde_json::from_str::(s).map_err(|e| e.to_string()) - } - - /// Returns true if the Variable is an Array. Returns false otherwise. - pub fn is_array(&self) -> bool { - self.as_array().is_some() - } - - /// If the Variable value is an Array, returns the associated vector. - /// Returns None otherwise. - pub fn as_array(&self) -> Option<&Vec> { - match self { - Variable::Array(array) => Some(array), - _ => None, - } - } - - /// Returns true if the value is an Object. - pub fn is_object(&self) -> bool { - self.as_object().is_some() - } - - /// If the value is an Object, returns the associated BTreeMap. - /// Returns None otherwise. - pub fn as_object(&self) -> Option<&BTreeMap> { - match self { - Variable::Object(map) => Some(map), - _ => None, - } - } - - /// Returns true if the value is a String. Returns false otherwise. - pub fn is_string(&self) -> bool { - self.as_string().is_some() - } - - /// If the value is a String, returns the associated str. - /// Returns None otherwise. - pub fn as_string(&self) -> Option<&String> { - match self { - Variable::String(ref s) => Some(s), - _ => None, - } - } - - /// Returns true if the value is a Number. Returns false otherwise. - pub fn is_number(&self) -> bool { - matches!(self, Variable::Number(_)) - } - - /// If the value is a number, return or cast it to a f64. - /// Returns None otherwise. - pub fn as_number(&self) -> Option { - match self { - Variable::Number(f) => f.as_f64(), - _ => None, - } - } - - /// Returns true if the value is a Boolean. Returns false otherwise. - pub fn is_boolean(&self) -> bool { - self.as_boolean().is_some() - } - - /// If the value is a Boolean, returns the associated bool. - /// Returns None otherwise. - pub fn as_boolean(&self) -> Option { - match self { - Variable::Bool(b) => Some(*b), - _ => None, - } - } - - /// Returns true if the value is a Null. Returns false otherwise. - pub fn is_null(&self) -> bool { - self.as_null().is_some() - } - - /// If the value is a Null, returns (). - /// Returns None otherwise. - pub fn as_null(&self) -> Option<()> { - match self { - Variable::Null => Some(()), - _ => None, - } - } - - /// Returns true if the value is an expression reference. - /// Returns false otherwise. - pub fn is_expref(&self) -> bool { - self.as_expref().is_some() - } - - /// If the value is an expression reference, returns the associated Ast node. - /// Returns None otherwise. - pub fn as_expref(&self) -> Option<&Ast> { - match *self { - Variable::Expref(ref ast) => Some(ast), - _ => None, - } - } - - /// If the value is an object, returns the value associated with the provided key. - /// Otherwise, returns Null. - #[inline] - pub fn get_field(&self, key: &str) -> Rcvar { - if let Variable::Object(ref map) = self { - if let Some(result) = map.get(key) { - return result.clone(); - } - } - Rcvar::new(Variable::Null) - } - - /// If the value is an array, then gets an array value by index. Otherwise returns Null. - #[inline] - pub fn get_index(&self, index: usize) -> Rcvar { - if let Variable::Array(ref array) = self { - if let Some(result) = array.get(index) { - return result.clone(); - } - } - Rcvar::new(Variable::Null) - } - - /// Retrieves an index from the end of an array. - /// - /// Returns Null if not an array or if the index is not present. - /// The formula for determining the index position is length - index (i.e., an - /// index of 0 or 1 is treated as the end of the array). - pub fn get_negative_index(&self, index: usize) -> Rcvar { - if let Variable::Array(ref array) = self { - let adjusted_index = max(index, 1); - if array.len() >= adjusted_index { - return array[array.len() - adjusted_index].clone(); - } - } - Rcvar::new(Variable::Null) - } - - /// Returns true or false based on if the Variable value is considered truthy. - pub fn is_truthy(&self) -> bool { - match self { - Variable::Bool(b) => *b, - Variable::String(ref s) => !s.is_empty(), - Variable::Array(ref a) => !a.is_empty(), - Variable::Object(ref o) => !o.is_empty(), - Variable::Number(_) => true, - _ => false, - } - } - - /// Returns the JMESPath type name of a Variable value. - pub fn get_type(&self) -> JmespathType { - match *self { - Variable::Bool(_) => JmespathType::Boolean, - Variable::String(_) => JmespathType::String, - Variable::Number(_) => JmespathType::Number, - Variable::Array(_) => JmespathType::Array, - Variable::Object(_) => JmespathType::Object, - Variable::Null => JmespathType::Null, - Variable::Expref(_) => JmespathType::Expref, - } - } - - /// Compares two Variable values using a comparator. - pub fn compare(&self, cmp: &Comparator, value: &Variable) -> Option { - // Ordering requires numeric values. - if !(self.is_number() && value.is_number() - || *cmp == Comparator::NotEqual - || *cmp == Comparator::Equal) - { - return None; - } - match *cmp { - Comparator::Equal => Some(*self == *value), - Comparator::NotEqual => Some(*self != *value), - Comparator::LessThan => Some(*self < *value), - Comparator::LessThanEqual => Some(*self <= *value), - Comparator::GreaterThan => Some(*self > *value), - Comparator::GreaterThanEqual => Some(*self >= *value), - } - } - - /// Returns a slice of the variable if the variable is an array. - pub fn slice(&self, start: Option, stop: Option, step: i32) -> Option> { - self.as_array().map(|a| slice(a, start, stop, step)) - } -} - -impl Variable { - fn unexpected(&self) -> de::Unexpected<'_> { - match self { - Variable::Null => de::Unexpected::Unit, - Variable::Bool(b) => de::Unexpected::Bool(*b), - Variable::Number(_) => de::Unexpected::Other("number"), - Variable::String(s) => de::Unexpected::Str(s), - Variable::Array(_) => de::Unexpected::Seq, - Variable::Object(_) => de::Unexpected::Map, - Variable::Expref(_) => de::Unexpected::Other("expression"), - } - } -} - -// ------------------------------------------ -// Variable slicing implementation -// ------------------------------------------ - -fn slice(array: &[Rcvar], start: Option, stop: Option, step: i32) -> Vec { - let mut result = vec![]; - let len = array.len() as i32; - if len == 0 { - return result; - } - let a: i32 = match start { - Some(starting_index) => adjust_slice_endpoint(len, starting_index, step), - _ if step < 0 => len - 1, - _ => 0, - }; - let b: i32 = match stop { - Some(ending_index) => adjust_slice_endpoint(len, ending_index, step), - _ if step < 0 => -1, - _ => len, - }; - let mut i = a; - if step > 0 { - while i < b { - result.push(array[i as usize].clone()); - i += step; - } - } else { - while i > b { - result.push(array[i as usize].clone()); - i += step; - } - } - result -} - -#[inline] -fn adjust_slice_endpoint(len: i32, mut endpoint: i32, step: i32) -> i32 { - if endpoint < 0 { - endpoint += len; - if endpoint >= 0 { - endpoint - } else if step < 0 { - -1 - } else { - 0 - } - } else if endpoint < len { - endpoint - } else if step < 0 { - len - 1 - } else { - len - } -} - -// Serde Variable deserialization -// ------------------------------------------ -// Below begins the amazingly complicated code needed to implement -// serde serialization and deserialization for Variable. This code -// is ported from the serde_json::Value implementation and adapted -// for Variable and Rcvar. There are a few instances of -// `(*self).clone` that I don't like, but I'm not sure how to work -// around it. - -/// Shortcut function to encode a `T` into a JMESPath `Variable` -fn to_variable(value: T) -> Result -where - T: ser::Serialize, -{ - value.serialize(Serializer) -} - -/// Serde deserialization for Variable -impl<'de> de::Deserialize<'de> for Variable { - #[inline] - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - struct VariableVisitor; - - impl<'de> de::Visitor<'de> for VariableVisitor { - type Value = Variable; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("any valid JMESPath variable") - } - - #[inline] - fn visit_bool(self, value: bool) -> Result { - Ok(Variable::Bool(value)) - } - - #[inline] - fn visit_i64(self, value: i64) -> Result { - Ok(Variable::Number(value.into())) - } - - #[inline] - fn visit_u64(self, value: u64) -> Result { - Ok(Variable::Number(value.into())) - } - - #[inline] - fn visit_f64(self, value: f64) -> Result { - Ok(Number::from_f64(value).map_or(Variable::Null, Variable::Number)) - } - - #[inline] - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - self.visit_string(String::from(value)) - } - - #[inline] - fn visit_string(self, value: String) -> Result { - Ok(Variable::String(value)) - } - - #[inline] - fn visit_none(self) -> Result { - Ok(Variable::Null) - } - - #[inline] - fn visit_some(self, deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - de::Deserialize::deserialize(deserializer) - } - - #[inline] - fn visit_unit(self) -> Result { - Ok(Variable::Null) - } - - #[inline] - fn visit_seq(self, mut visitor: V) -> Result - where - V: de::SeqAccess<'de>, - { - let mut values = vec![]; - - while let Some(elem) = visitor.next_element()? { - values.push(elem); - } - - Ok(Variable::Array(values)) - } - - #[inline] - fn visit_map(self, mut visitor: V) -> Result - where - V: de::MapAccess<'de>, - { - let mut values = BTreeMap::new(); - - while let Some((key, value)) = visitor.next_entry()? { - values.insert(key, value); - } - - Ok(Variable::Object(values)) - } - } - - deserializer.deserialize_any(VariableVisitor) - } -} - -impl<'de> de::Deserializer<'de> for Variable { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - match self { - Variable::Null => visitor.visit_unit(), - Variable::Bool(v) => visitor.visit_bool(v), - Variable::Number(v) => v.deserialize_any(visitor), - Variable::String(v) => visitor.visit_string(v), - Variable::Array(v) => { - let len = v.len(); - visitor.visit_seq(SeqDeserializer { - iter: v.into_iter(), - len, - }) - } - Variable::Object(v) => visitor.visit_map(MapDeserializer { - iter: v.into_iter(), - value: None, - }), - Variable::Expref(v) => visitor.visit_string(format!("", v)), - } - } - - #[inline] - fn deserialize_option(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - match self { - Variable::Null => visitor.visit_none(), - _ => visitor.visit_some(self), - } - } - - #[inline] - fn deserialize_enum( - self, - _name: &str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: de::Visitor<'de>, - { - let (variant, value) = match self { - Variable::Object(value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(de::Error::invalid_value( - de::Unexpected::Map, - &"map with a single key", - )) - } - }; - // enums are encoded in json as maps with a single key:value pair - if iter.next().is_some() { - return Err(de::Error::invalid_value( - de::Unexpected::Map, - &"map with a single key", - )); - } - (variant, Some((*value).clone())) - } - Variable::String(variant) => (variant, None), - other => { - return Err(de::Error::invalid_type( - other.unexpected(), - &"string or map", - )); - } - }; - - visitor.visit_enum(EnumDeserializer { - val: value, - variant, - }) - } - - #[inline] - fn deserialize_newtype_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: de::Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - forward_to_deserialize_any! { - bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string - unit seq bytes byte_buf map unit_struct tuple_struct struct - identifier tuple ignored_any - } -} - -struct VariantDeserializer { - val: Option, -} - -impl<'de> de::VariantAccess<'de> for VariantDeserializer { - type Error = Error; - - fn unit_variant(self) -> Result<(), Error> { - match self.val { - Some(value) => de::Deserialize::deserialize(value), - None => Ok(()), - } - } - - fn newtype_variant_seed(self, seed: T) -> Result - where - T: de::DeserializeSeed<'de>, - { - match self.val { - Some(value) => seed.deserialize(value), - None => Err(de::Error::invalid_type( - de::Unexpected::UnitVariant, - &"newtype variant", - )), - } - } - - fn tuple_variant(self, _len: usize, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - match self.val { - Some(Variable::Array(fields)) => de::Deserializer::deserialize_any( - SeqDeserializer { - len: fields.len(), - iter: fields.into_iter(), - }, - visitor, - ), - Some(other) => Err(de::Error::invalid_type( - other.unexpected(), - &"tuple variant", - )), - None => Err(de::Error::invalid_type( - de::Unexpected::UnitVariant, - &"tuple variant", - )), - } - } - - fn struct_variant( - self, - _fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: de::Visitor<'de>, - { - match self.val { - Some(Variable::Object(fields)) => de::Deserializer::deserialize_any( - MapDeserializer { - iter: fields.into_iter(), - value: None, - }, - visitor, - ), - Some(other) => Err(de::Error::invalid_type( - other.unexpected(), - &"struct variant", - )), - _ => Err(de::Error::invalid_type( - de::Unexpected::UnitVariant, - &"struct variant", - )), - } - } -} - -struct EnumDeserializer { - variant: String, - val: Option, -} - -impl<'de> de::EnumAccess<'de> for EnumDeserializer { - type Error = Error; - type Variant = VariantDeserializer; - - fn variant_seed(self, seed: V) -> Result<(V::Value, VariantDeserializer), Error> - where - V: de::DeserializeSeed<'de>, - { - let variant = self.variant.into_deserializer(); - let visitor = VariantDeserializer { val: self.val }; - seed.deserialize(variant).map(|v| (v, visitor)) - } -} - -struct SeqDeserializer { - iter: vec::IntoIter, - len: usize, -} - -impl<'de> de::Deserializer<'de> for SeqDeserializer { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - if self.len == 0 { - visitor.visit_unit() - } else { - visitor.visit_seq(self) - } - } - - forward_to_deserialize_any! { - bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string - unit option seq bytes byte_buf map unit_struct newtype_struct - tuple_struct struct identifier tuple enum ignored_any - } -} - -impl<'de> de::SeqAccess<'de> for SeqDeserializer { - type Error = Error; - - fn next_element_seed(&mut self, seed: T) -> Result, Error> - where - T: de::DeserializeSeed<'de>, - { - match self.iter.next() { - Some(value) => seed.deserialize(Variable::clone(&value)).map(Some), - None => Ok(None), - } - } - - fn size_hint(&self) -> Option { - match self.iter.size_hint() { - (lower, Some(upper)) if lower == upper => Some(upper), - _ => None, - } - } -} - -struct MapDeserializer { - iter: as IntoIterator>::IntoIter, - value: Option, -} - -impl<'de> de::MapAccess<'de> for MapDeserializer { - type Error = Error; - - fn next_key_seed(&mut self, seed: T) -> Result, Error> - where - T: de::DeserializeSeed<'de>, - { - match self.iter.next() { - Some((key, value)) => { - self.value = Some(Variable::clone(&value)); - seed.deserialize(Variable::String(key)).map(Some) - } - None => Ok(None), - } - } - - fn next_value_seed(&mut self, seed: T) -> Result - where - T: de::DeserializeSeed<'de>, - { - match self.value.take() { - Some(value) => seed.deserialize(value), - None => Err(de::Error::custom("value is missing")), - } - } - - fn size_hint(&self) -> Option { - match self.iter.size_hint() { - (lower, Some(upper)) if lower == upper => Some(upper), - _ => None, - } - } -} - -impl<'de> de::Deserializer<'de> for MapDeserializer { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string - unit option seq bytes byte_buf map unit_struct newtype_struct - tuple_struct struct identifier tuple enum ignored_any - } -} - -// Serde Variable serialization -impl ser::Serialize for Variable { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - match self { - Variable::Null => serializer.serialize_unit(), - Variable::Bool(v) => serializer.serialize_bool(*v), - Variable::Number(v) => v.serialize(serializer), - Variable::String(ref v) => serializer.serialize_str(v), - Variable::Array(ref v) => v.serialize(serializer), - Variable::Object(ref v) => v.serialize(serializer), - Variable::Expref(ref e) => serializer.serialize_str(&format!("", e)), - } - } -} - -/// Create a `serde::Serializer` that serializes a `Serialize`e into a `Variable`. -#[derive(Debug, Default)] -pub struct Serializer; - -#[doc(hidden)] -pub struct SeqState(Vec); - -#[doc(hidden)] -pub struct TupleVariantState { - name: String, - vec: Vec, -} - -#[doc(hidden)] -pub struct StructVariantState { - name: String, - map: BTreeMap, -} - -#[doc(hidden)] -pub struct MapState { - map: BTreeMap, - next_key: Option, -} - -impl ser::Serializer for Serializer { - type Ok = Variable; - type Error = Error; - - type SerializeSeq = SeqState; - type SerializeTuple = SeqState; - type SerializeTupleStruct = SeqState; - type SerializeTupleVariant = TupleVariantState; - type SerializeMap = MapState; - type SerializeStruct = MapState; - type SerializeStructVariant = StructVariantState; - - #[inline] - fn serialize_bool(self, value: bool) -> Result { - Ok(Variable::Bool(value)) - } - - #[inline] - fn serialize_i8(self, value: i8) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_i16(self, value: i16) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_i32(self, value: i32) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - fn serialize_i64(self, value: i64) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_u8(self, value: u8) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_u16(self, value: u16) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_u32(self, value: u32) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_u64(self, value: u64) -> Result { - Ok(Variable::Number(Number::from(value))) - } - - #[inline] - fn serialize_f32(self, value: f32) -> Result { - self.serialize_f64(value as f64) - } - - #[inline] - fn serialize_f64(self, value: f64) -> Result { - Ok(Number::from_f64(value).map_or(Variable::Null, Variable::Number)) - } - - #[inline] - fn serialize_char(self, value: char) -> Result { - let mut s = String::new(); - s.push(value); - self.serialize_str(&s) - } - - #[inline] - fn serialize_str(self, value: &str) -> Result { - Ok(Variable::String(String::from(value))) - } - - fn serialize_bytes(self, value: &[u8]) -> Result { - let vec = value - .iter() - .map(|&b| Rcvar::new(Variable::Number(Number::from(b)))) - .collect(); - Ok(Variable::Array(vec)) - } - - #[inline] - fn serialize_unit(self) -> Result { - Ok(Variable::Null) - } - - #[inline] - fn serialize_unit_struct(self, _name: &'static str) -> Result { - self.serialize_unit() - } - - #[inline] - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> Result { - self.serialize_str(variant) - } - - #[inline] - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result - where - T: ser::Serialize, - { - value.serialize(self) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - value: &T, - ) -> Result - where - T: ser::Serialize, - { - let mut values = BTreeMap::new(); - values.insert(String::from(variant), Rcvar::new(to_variable(&value)?)); - Ok(Variable::Object(values)) - } - - #[inline] - fn serialize_none(self) -> Result { - self.serialize_unit() - } - - #[inline] - fn serialize_some(self, value: &V) -> Result - where - V: ser::Serialize, - { - value.serialize(self) - } - - fn serialize_seq(self, len: Option) -> Result { - Ok(SeqState(Vec::with_capacity(len.unwrap_or(0)))) - } - - fn serialize_tuple(self, len: usize) -> Result { - self.serialize_seq(Some(len)) - } - - fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { - self.serialize_seq(Some(len)) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - Ok(TupleVariantState { - name: String::from(variant), - vec: Vec::with_capacity(len), - }) - } - - fn serialize_map(self, _len: Option) -> Result { - Ok(MapState { - map: BTreeMap::new(), - next_key: None, - }) - } - - fn serialize_struct(self, _name: &'static str, len: usize) -> Result { - self.serialize_map(Some(len)) - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Ok(StructVariantState { - name: String::from(variant), - map: BTreeMap::new(), - }) - } -} - -impl ser::SerializeSeq for SeqState { - type Ok = Variable; - type Error = Error; - - fn serialize_element(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - self.0.push(Rcvar::new(to_variable(value)?)); - Ok(()) - } - - fn end(self) -> Result { - Ok(Variable::Array(self.0)) - } -} - -impl ser::SerializeTuple for SeqState { - type Ok = Variable; - type Error = Error; - - fn serialize_element(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - ser::SerializeSeq::serialize_element(self, value) - } - - fn end(self) -> Result { - ser::SerializeSeq::end(self) - } -} - -impl ser::SerializeTupleStruct for SeqState { - type Ok = Variable; - type Error = Error; - - fn serialize_field(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - ser::SerializeSeq::serialize_element(self, value) - } - - fn end(self) -> Result { - ser::SerializeSeq::end(self) - } -} - -impl ser::SerializeTupleVariant for TupleVariantState { - type Ok = Variable; - type Error = Error; - - fn serialize_field(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - self.vec.push(Rcvar::new(to_variable(&value)?)); - Ok(()) - } - - fn end(self) -> Result { - let mut object = BTreeMap::new(); - object.insert(self.name, Rcvar::new(Variable::Array(self.vec))); - Ok(Variable::Object(object)) - } -} - -impl ser::SerializeMap for MapState { - type Ok = Variable; - type Error = Error; - - fn serialize_key(&mut self, key: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - match to_variable(key)? { - Variable::String(s) => self.next_key = Some(s), - _ => return Err(de::Error::custom("KeyMustBeAString")), - }; - Ok(()) - } - - fn serialize_value(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - let key = self - .next_key - .take() - .expect("serialize_value called before serialize_key"); - self.map.insert(key, Rcvar::new(to_variable(value)?)); - Ok(()) - } - - fn end(self) -> Result { - Ok(Variable::Object(self.map)) - } -} - -impl ser::SerializeStruct for MapState { - type Ok = Variable; - type Error = Error; - - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - ser::SerializeMap::serialize_key(self, key)?; - ser::SerializeMap::serialize_value(self, value) - } - - fn end(self) -> Result { - ser::SerializeMap::end(self) - } -} - -impl ser::SerializeStructVariant for StructVariantState { - type Ok = Variable; - type Error = Error; - - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - self.map - .insert(String::from(key), Rcvar::new(to_variable(value)?)); - Ok(()) - } - - fn end(self) -> Result { - let mut object = BTreeMap::new(); - object.insert(self.name, Rcvar::new(Variable::Object(self.map))); - Ok(Variable::Object(object)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::ast::{Ast, Comparator}; - use crate::Rcvar; - use serde_json::{self, Number, Value}; - use std::collections::BTreeMap; - - #[test] - fn creates_variable_from_str() { - assert_eq!(Ok(Variable::Bool(true)), Variable::from_json("true")); - assert_eq!( - Err("expected value at line 1 column 1".to_string()), - Variable::from_json("abc") - ); - } - - #[test] - fn test_determines_types() { - assert_eq!( - JmespathType::Object, - Variable::from_json(&"{\"foo\": \"bar\"}") - .unwrap() - .get_type() - ); - assert_eq!( - JmespathType::Array, - Variable::from_json(&"[\"foo\"]").unwrap().get_type() - ); - assert_eq!(JmespathType::Null, Variable::Null.get_type()); - assert_eq!(JmespathType::Boolean, Variable::Bool(true).get_type()); - assert_eq!( - JmespathType::String, - Variable::String("foo".to_string()).get_type() - ); - assert_eq!( - JmespathType::Number, - Variable::Number(Number::from_f64(1.0).unwrap()).get_type() - ); - } - - #[test] - fn test_is_truthy() { - assert_eq!( - true, - Variable::from_json(&"{\"foo\": \"bar\"}") - .unwrap() - .is_truthy() - ); - assert_eq!(false, Variable::from_json(&"{}").unwrap().is_truthy()); - assert_eq!(true, Variable::from_json(&"[\"foo\"]").unwrap().is_truthy()); - assert_eq!(false, Variable::from_json(&"[]").unwrap().is_truthy()); - assert_eq!(false, Variable::Null.is_truthy()); - assert_eq!(true, Variable::Bool(true).is_truthy()); - assert_eq!(false, Variable::Bool(false).is_truthy()); - assert_eq!(true, Variable::String("foo".to_string()).is_truthy()); - assert_eq!(false, Variable::String("".to_string()).is_truthy()); - assert_eq!( - true, - Variable::Number(Number::from_f64(10.0).unwrap()).is_truthy() - ); - assert_eq!( - true, - Variable::Number(Number::from_f64(0.0).unwrap()).is_truthy() - ); - } - - #[test] - fn test_eq_ne_compare() { - let l = Variable::String("foo".to_string()); - let r = Variable::String("foo".to_string()); - assert_eq!(Some(true), l.compare(&Comparator::Equal, &r)); - assert_eq!(Some(false), l.compare(&Comparator::NotEqual, &r)); - } - - #[test] - fn test_compare() { - let invalid = Variable::String("foo".to_string()); - let l = Variable::Number(Number::from_f64(10.0).unwrap()); - let r = Variable::Number(Number::from_f64(20.0).unwrap()); - assert_eq!(None, invalid.compare(&Comparator::GreaterThan, &r)); - assert_eq!(Some(false), l.compare(&Comparator::GreaterThan, &r)); - assert_eq!(Some(false), l.compare(&Comparator::GreaterThanEqual, &r)); - assert_eq!(Some(true), r.compare(&Comparator::GreaterThan, &l)); - assert_eq!(Some(true), r.compare(&Comparator::GreaterThanEqual, &l)); - assert_eq!(Some(true), l.compare(&Comparator::LessThan, &r)); - assert_eq!(Some(true), l.compare(&Comparator::LessThanEqual, &r)); - assert_eq!(Some(false), r.compare(&Comparator::LessThan, &l)); - assert_eq!(Some(false), r.compare(&Comparator::LessThanEqual, &l)); - } - - #[test] - fn gets_value_from_object() { - let var = Variable::from_json("{\"foo\":1}").unwrap(); - assert_eq!( - Rcvar::new(Variable::Number(Number::from_f64(1.0).unwrap())), - var.get_field("foo") - ); - } - - #[test] - fn getting_value_from_non_object_is_null() { - assert_eq!( - Rcvar::new(Variable::Null), - Variable::Bool(false).get_field("foo") - ); - } - - #[test] - fn determines_if_null() { - assert_eq!(false, Variable::Bool(true).is_null()); - assert_eq!(true, Variable::Null.is_null()); - } - - #[test] - fn option_of_null() { - assert_eq!(Some(()), Variable::Null.as_null()); - assert_eq!(None, Variable::Bool(true).as_null()); - } - - #[test] - fn determines_if_boolean() { - assert_eq!(true, Variable::Bool(true).is_boolean()); - assert_eq!(false, Variable::Null.is_boolean()); - } - - #[test] - fn option_of_boolean() { - assert_eq!(Some(true), Variable::Bool(true).as_boolean()); - assert_eq!(None, Variable::Null.as_boolean()); - } - - #[test] - fn determines_if_string() { - assert_eq!(false, Variable::Bool(true).is_string()); - assert_eq!(true, Variable::String("foo".to_string()).is_string()); - } - - #[test] - fn option_of_string() { - assert_eq!( - Some(&"foo".to_string()), - Variable::String("foo".to_string()).as_string() - ); - assert_eq!(None, Variable::Null.as_string()); - } - - #[test] - fn test_is_number() { - let value = Variable::from_json("12").unwrap(); - assert!(value.is_number()); - } - - #[test] - fn test_as_number() { - let value = Variable::from_json("12.0").unwrap(); - assert_eq!(value.as_number(), Some(12f64)); - } - - #[test] - fn test_is_object() { - let value = Variable::from_json("{}").unwrap(); - assert!(value.is_object()); - } - - #[test] - fn test_as_object() { - let value = Variable::from_json("{}").unwrap(); - assert!(value.as_object().is_some()); - } - - #[test] - fn test_is_array() { - let value = Variable::from_json("[1, 2, 3]").unwrap(); - assert!(value.is_array()); - } - - #[test] - fn test_as_array() { - let value = Variable::from_json("[1, 2, 3]").unwrap(); - let array = value.as_array(); - let expected_length = 3; - assert!(array.is_some() && array.unwrap().len() == expected_length); - } - - #[test] - fn test_is_expref() { - assert_eq!( - true, - Variable::Expref(Ast::Identity { offset: 0 }).is_expref() - ); - assert_eq!( - &Ast::Identity { offset: 0 }, - Variable::Expref(Ast::Identity { offset: 0 }) - .as_expref() - .unwrap() - ); - } - - #[test] - fn test_parses_json_scalar() { - assert_eq!(Variable::Null, Variable::from_json("null").unwrap()); - assert_eq!(Variable::Bool(true), Variable::from_json("true").unwrap()); - assert_eq!(Variable::Bool(false), Variable::from_json("false").unwrap()); - assert_eq!( - Variable::Number(Number::from(1)), - Variable::from_json("1").unwrap() - ); - assert_eq!( - Variable::Number(Number::from_f64(-1.0).unwrap()), - Variable::from_json("-1").unwrap() - ); - assert_eq!( - Variable::Number(Number::from_f64(1.5).unwrap()), - Variable::from_json("1.5").unwrap() - ); - assert_eq!( - Variable::String("abc".to_string()), - Variable::from_json("\"abc\"").unwrap() - ); - } - - #[test] - fn test_parses_and_encodes_complex() { - let js = "[null,true,1,[\"a\"],{\"b\":{\"c\":[[9.9],false]}},-1,1.0000001]"; - let var = Variable::from_json(js).unwrap(); - assert_eq!(js, var.to_string()); - } - - #[test] - fn test_parses_json_object() { - let var = Variable::from_json("{\"a\": 1, \"b\": {\"c\": true}}").unwrap(); - let mut expected = BTreeMap::new(); - let mut sub_obj = BTreeMap::new(); - expected.insert( - "a".to_string(), - Rcvar::new(Variable::Number(Number::from_f64(1.0).unwrap())), - ); - sub_obj.insert("c".to_string(), Rcvar::new(Variable::Bool(true))); - expected.insert("b".to_string(), Rcvar::new(Variable::Object(sub_obj))); - assert_eq!(var, Variable::Object(expected)); - } - - #[test] - fn test_converts_to_json() { - let var = Variable::from_json("true").unwrap(); - let val = serde_json::to_value(&var).unwrap(); - assert_eq!(Value::Bool(true), val); - let round_trip = serde_json::from_value::(val).unwrap(); - assert_eq!(Variable::Bool(true), round_trip); - // Try a more complex shape - let var = Variable::from_json("[\"a\"]").unwrap(); - let val = serde_json::to_value(&var).unwrap(); - assert_eq!(Value::Array(vec![Value::String("a".to_string())]), val); - let round_trip = serde_json::from_value::(val).unwrap(); - assert_eq!( - Variable::Array(vec![Rcvar::new(Variable::String("a".to_string()))]), - round_trip - ); - } - - /// Converting an expression variable to a string is a special case. - #[test] - fn test_converts_to_string() { - assert_eq!("true", Variable::Bool(true).to_string()); - assert_eq!("[true]", Variable::from_json("[true]").unwrap().to_string()); - let v = Variable::Expref(Ast::Identity { offset: 0 }); - assert_eq!("\"\"", v.to_string()); - } - - #[test] - fn test_compares_float_equality() { - assert_eq!( - Variable::Number(Number::from_f64(1.0).unwrap()), - Variable::Number(Number::from_f64(1.0).unwrap()) - ); - assert_eq!( - Variable::Number(Number::from_f64(0.0).unwrap()), - Variable::Number(Number::from_f64(0.0).unwrap()) - ); - assert_ne!( - Variable::Number(Number::from_f64(0.00001).unwrap()), - Variable::Number(Number::from_f64(0.0).unwrap()) - ); - assert_eq!( - Variable::Number(Number::from_f64(999.999).unwrap()), - Variable::Number(Number::from_f64(999.999).unwrap()) - ); - assert_eq!( - Variable::Number(Number::from_f64(1.000000000001).unwrap()), - Variable::Number(Number::from_f64(1.000000000001).unwrap()) - ); - assert_eq!( - Variable::Number(Number::from_f64(0.7100000000000002).unwrap()), - Variable::Number(Number::from_f64(0.71).unwrap()) - ); - assert_eq!( - Variable::Number(Number::from_f64(0.0000000000000002).unwrap()), - Variable::Number(Number::from_f64(0.0000000000000002).unwrap()) - ); - assert_ne!( - Variable::Number(Number::from_f64(0.0000000000000002).unwrap()), - Variable::Number(Number::from_f64(0.0000000000000003).unwrap()) - ); - } - - #[test] - fn test_serialize_variable_with_positive_integer_value() { - #[derive(serde_derive::Serialize)] - struct Map { - num: usize, - } - - let map = Map { num: 231 }; - - let var = Variable::from_serializable(&map).unwrap(); - let json_string = serde_json::to_string(&var).unwrap(); - - assert_eq!(r#"{"num":231}"#, json_string); - } - - #[test] - fn test_serialize_variable_with_negative_integer_value() { - #[derive(serde_derive::Serialize)] - struct Map { - num: isize, - } - - let map = Map { num: -2141 }; - - let var = Variable::from_serializable(&map).unwrap(); - let json_string = serde_json::to_string(&var).unwrap(); - - assert_eq!(r#"{"num":-2141}"#, json_string); - } - - #[test] - fn test_serialize_variable_with_float_value() { - #[derive(serde_derive::Serialize)] - struct Map { - num: f64, - } - - let map = Map { num: 41.0 }; - - let var = Variable::from_serializable(&map).unwrap(); - let json_string = serde_json::to_string(&var).unwrap(); - - assert_eq!(r#"{"num":41.0}"#, json_string); - } -} diff --git a/lib/jmespath/jmespath/tests/compliance.rs b/lib/jmespath/jmespath/tests/compliance.rs deleted file mode 100644 index 1175353..0000000 --- a/lib/jmespath/jmespath/tests/compliance.rs +++ /dev/null @@ -1,328 +0,0 @@ -//! JMESPath compliance tests. -//! -//! Test cases are generated using build.rs. This may eventually be exposed -//! as a library (leading to possibilities like a compliance test runner CLI). - -use serde_json::Value; -use std::fmt; - -use jmespath::{compile, Expression, Rcvar, RuntimeError, Variable}; - -/// Avaliable benchmark types. -pub enum BenchType { - /// The benchmark must only parse an expression. - Parse, - /// The benchmark must benchmark only the interpreter - Interpret, - /// The benchmark must benchmark both the parser and interpreter. - /// JMESPath.rs will benchmark an entire execute, parsing, interpreting separately. - Full, -} - -impl BenchType { - /// Try to create a benchmark assertion from a JSON value. - fn from_json(bench_type: &Value) -> Result { - bench_type - .as_str() - .ok_or(TestCaseError::BenchIsNotString) - .and_then(|b| match b { - "parse" => Ok(BenchType::Parse), - "interpret" => Ok(BenchType::Interpret), - "full" => Ok(BenchType::Full), - s @ _ => Err(TestCaseError::UnknownBenchType(s.to_string())), - }) - } -} - -impl fmt::Display for BenchType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - &BenchType::Parse => write!(fmt, "parse"), - &BenchType::Interpret => write!(fmt, "interpret"), - &BenchType::Full => write!(fmt, "full"), - } - } -} - -/// Avaliable error types. -pub enum ErrorType { - /// Ensures that the expression fails due to an invalid-arity error. - InvalidArity, - /// Ensures that the expression fails due to an invalid-type error. - InvalidType, - /// Ensures that the expression fails due to an invalid-value error. - InvalidSlice, - /// Ensures that the expression fails due to an unknown-function error. - UnknownFunction, - /// Ensures that an expression cannot be parsed due to a syntax error. - SyntaxError, -} - -impl ErrorType { - /// Try to create an error assertion from a JSON value. - fn from_json(error_type: &Value) -> Result { - error_type - .as_str() - .ok_or(TestCaseError::ErrorIsNotString) - .and_then(|b| match b { - "syntax" => Ok(ErrorType::SyntaxError), - "invalid-type" => Ok(ErrorType::InvalidType), - "invalid-value" => Ok(ErrorType::InvalidSlice), - "invalid-arity" => Ok(ErrorType::InvalidArity), - "unknown-function" => Ok(ErrorType::UnknownFunction), - e @ _ => Err(TestCaseError::UnknownErrorType(e.to_string())), - }) - } -} - -impl fmt::Display for ErrorType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - use self::ErrorType::*; - match self { - &InvalidArity => write!(fmt, "invalid-arity"), - &InvalidType => write!(fmt, "invalid-type"), - &InvalidSlice => write!(fmt, "invalid-value"), - &UnknownFunction => write!(fmt, "unknown-function"), - &SyntaxError => write!(fmt, "syntax"), - } - } -} - -/// Test case assertions. -pub enum Assertion { - /// Ensures that a test fails with a particular error type. - Error(ErrorType), - /// Ignores the result and marks the test as a benchmark - Bench(BenchType), - /// Ensures that the expression is parsed and returns an expected result. - ValidResult(Rcvar), -} - -impl Assertion { - /// Runs the assertion of a test case - pub fn assert(&self, suite: &str, case: &TestCase, given: Rcvar) -> Result<(), String> { - match self { - &Assertion::Bench(_) => Ok(()), - &Assertion::ValidResult(ref expected_result) => { - let expr = self.try_parse(suite, case)?; - match expr.search(given) { - Err(e) => Err(self.err_message(suite, case, format!("{}", e))), - Ok(r) => { - if *r == **expected_result { - Ok(()) - } else { - Err(self.err_message( - suite, - case, - format!("{:?}, {}", r, expr.as_ast()), - )) - } - } - } - } - &Assertion::Error(ref error_type) => { - use jmespath::ErrorReason::*; - let result = self.try_parse(suite, case); - match error_type { - &ErrorType::InvalidArity => match result?.search(given).map_err(|e| e.reason) { - Err(Runtime(RuntimeError::NotEnoughArguments { .. })) => Ok(()), - Err(Runtime(RuntimeError::TooManyArguments { .. })) => Ok(()), - Err(e) => Err(self.err_message(suite, case, format!("{}", e))), - Ok(r) => Err(self.err_message(suite, case, r.to_string())), - }, - &ErrorType::InvalidType => match result?.search(given).map_err(|e| e.reason) { - Err(Runtime(RuntimeError::InvalidType { .. })) => Ok(()), - Err(Runtime(RuntimeError::InvalidReturnType { .. })) => Ok(()), - Err(e) => Err(self.err_message(suite, case, format!("{}", e))), - Ok(r) => Err(self.err_message(suite, case, r.to_string())), - }, - &ErrorType::InvalidSlice => match result?.search(given).map_err(|e| e.reason) { - Err(Runtime(RuntimeError::InvalidSlice)) => Ok(()), - Err(e) => Err(self.err_message(suite, case, format!("{}", e))), - Ok(r) => Err(self.err_message(suite, case, r.to_string())), - }, - &ErrorType::UnknownFunction => { - match result?.search(given).map_err(|e| e.reason) { - Err(Runtime(RuntimeError::UnknownFunction(_))) => Ok(()), - Err(e) => Err(self.err_message(suite, case, format!("{}", e))), - Ok(r) => Err(self.err_message(suite, case, r.to_string())), - } - } - &ErrorType::SyntaxError => match result { - Err(_) => Ok(()), - Ok(expr) => { - Err(self.err_message(suite, case, format!("Parsed {:?}", expr))) - } - }, - } - } - } - } - - /// Attempts to parse an expression for a case, returning the expression or an error string. - fn try_parse(&self, suite: &str, case: &TestCase) -> Result, String> { - match compile(&case.expression) { - Err(e) => Err(self.err_message(suite, case, format!("{}", e))), - Ok(expr) => Ok(expr), - } - } - - /// Formats an error message for a test case failure. - fn err_message(&self, suite: &str, case: &TestCase, message: String) -> String { - format!( - "Test suite: {}\nExpression: {}\nAssertion: {}\nResult: {}\n==============", - suite, case.expression, self, message - ) - .to_string() - } -} - -impl fmt::Display for Assertion { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - &Assertion::Error(ref e) => write!(fmt, "expects error({})", e), - &Assertion::Bench(ref b) => write!(fmt, "expects bench({})", b), - &Assertion::ValidResult(ref r) => write!(fmt, "expects result({:?})", r), - } - } -} - -/// The test suite holds a collection of test cases and has a given value. -#[allow(dead_code)] -pub struct TestSuite<'a> { - /// Filename of the test suite - filename: &'a str, - /// Given data of the test suite - given: Rcvar, - /// Collection of test cases to perform - cases: Vec, -} - -impl<'a> TestSuite<'a> { - /// Creates a test suite from JSON string. - pub fn from_str(filename: &'a str, suite: &str) -> Result, String> { - serde_json::from_str::(suite) - .map_err(|e| e.to_string()) - .and_then(|j| TestSuite::from_json(filename, &j)) - } - - /// Creates a test suite from parsed JSON data. - fn from_json(filename: &'a str, suite: &Value) -> Result, String> { - let suite = suite.as_object().ok_or("test suite is not an object")?; - let test_case = suite.get("cases").ok_or("No cases value".to_string())?; - let case_array = test_case - .as_array() - .ok_or("cases is not an array".to_string())?; - let mut cases = vec![]; - for case in case_array { - cases.push(TestCase::from_json(case).map_err(|e| e.to_string())?); - } - let value = suite - .get("given") - .ok_or("No given value".to_string())? - .clone(); - let given = serde_json::from_value::(value).map_err(|e| format!("{}", e))?; - Ok(TestSuite { - filename: filename, - given: Rcvar::new(given), - cases: cases, - }) - } -} - -/// Errors that can occur when creating a TestCase -pub enum TestCaseError { - InvalidJSON(String), - NoCaseType, - NoResult, - ResultCannotToString, - NoExpression, - ExpressionIsNotString, - ErrorIsNotString, - UnknownErrorType(String), - UnknownBenchType(String), - BenchIsNotString, -} - -impl fmt::Display for TestCaseError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - use self::TestCaseError::*; - match self { - &InvalidJSON(ref msg) => write!(fmt, "invalid test case JSON: {}", msg), - &NoCaseType => write!(fmt, "case has no result, error, or bench"), - &NoResult => write!(fmt, "test case has no result key"), - &ResultCannotToString => write!(fmt, "result could not be cast to string"), - &NoExpression => write!(fmt, "test case has no expression key"), - &ExpressionIsNotString => write!(fmt, "test case expression is not a string"), - &ErrorIsNotString => write!(fmt, "test case error value is not a string"), - &UnknownErrorType(ref t) => write!(fmt, "unknown error type: {}", t), - &BenchIsNotString => write!(fmt, "bench value is not a string"), - &UnknownBenchType(ref bench) => write!( - fmt, - "unknown bench value: {}, expected one of of parse|full", - bench - ), - } - } -} - -impl fmt::Debug for TestCaseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self) - } -} - -/// Represents a test case that contains an expression and assertion. -pub struct TestCase { - /// The expression being evaluated. - pub expression: String, - /// The assertion to perform for the test case. - pub assertion: Assertion, -} - -impl TestCase { - /// Creates a test case from a JSON encoded string. - pub fn from_str(case: &str) -> Result { - serde_json::from_str::(case) - .map_err(|e| TestCaseError::InvalidJSON(e.to_string())) - .and_then(|json| TestCase::from_json(&json)) - } - - /// Creates a test case from parsed JSON data. - fn from_json(case: &Value) -> Result { - use crate::TestCaseError::*; - let case = case - .as_object() - .ok_or(InvalidJSON("not an object".to_string()))?; - Ok(TestCase { - expression: case - .get("expression") - .ok_or(NoExpression) - .and_then(|expression| { - expression - .as_str() - .ok_or(ExpressionIsNotString) - .map(|expression_str| expression_str.to_string()) - })?, - assertion: match case.get("error") { - Some(err) => Assertion::Error(ErrorType::from_json(err)?), - None if case.contains_key("result") => { - let value = case.get("result").unwrap(); - let var = serde_json::from_value::(value.clone()).unwrap(); - Assertion::ValidResult(Rcvar::new(var)) - } - None if case.contains_key("bench") => { - Assertion::Bench(BenchType::from_json(case.get("bench").unwrap())?) - } - _ => return Err(NoCaseType), - }, - }) - } - - /// Perform the test case assertion against a given value. - pub fn assert(&self, suite_filename: &str, given: Rcvar) -> Result<(), String> { - self.assertion.assert(suite_filename, self, given) - } -} - -include!(concat!(env!("OUT_DIR"), "/compliance_tests.rs")); diff --git a/lib/jmespath/jmespath/tests/compliance/basic.json b/lib/jmespath/jmespath/tests/compliance/basic.json deleted file mode 100644 index d550e96..0000000 --- a/lib/jmespath/jmespath/tests/compliance/basic.json +++ /dev/null @@ -1,96 +0,0 @@ -[{ - "given": - {"foo": {"bar": {"baz": "correct"}}}, - "cases": [ - { - "expression": "foo", - "result": {"bar": {"baz": "correct"}} - }, - { - "expression": "foo.bar", - "result": {"baz": "correct"} - }, - { - "expression": "foo.bar.baz", - "result": "correct" - }, - { - "expression": "foo\n.\nbar\n.baz", - "result": "correct" - }, - { - "expression": "foo.bar.baz.bad", - "result": null - }, - { - "expression": "foo.bar.bad", - "result": null - }, - { - "expression": "foo.bad", - "result": null - }, - { - "expression": "bad", - "result": null - }, - { - "expression": "bad.morebad.morebad", - "result": null - } - ] -}, -{ - "given": - {"foo": {"bar": ["one", "two", "three"]}}, - "cases": [ - { - "expression": "foo", - "result": {"bar": ["one", "two", "three"]} - }, - { - "expression": "foo.bar", - "result": ["one", "two", "three"] - } - ] -}, -{ - "given": ["one", "two", "three"], - "cases": [ - { - "expression": "one", - "result": null - }, - { - "expression": "two", - "result": null - }, - { - "expression": "three", - "result": null - }, - { - "expression": "one.two", - "result": null - } - ] -}, -{ - "given": - {"foo": {"1": ["one", "two", "three"], "-1": "bar"}}, - "cases": [ - { - "expression": "foo.\"1\"", - "result": ["one", "two", "three"] - }, - { - "expression": "foo.\"1\"[0]", - "result": "one" - }, - { - "expression": "foo.\"-1\"", - "result": "bar" - } - ] -} -] diff --git a/lib/jmespath/jmespath/tests/compliance/benchmarks.json b/lib/jmespath/jmespath/tests/compliance/benchmarks.json deleted file mode 100644 index 771953f..0000000 --- a/lib/jmespath/jmespath/tests/compliance/benchmarks.json +++ /dev/null @@ -1,124 +0,0 @@ -[ - { - "given": { - "long_name_for_a_field": true, - "a": { - "b": { - "c": { - "d": { - "e": { - "f": { - "g": { - "h": { - "i": { - "j": { - "k": { - "l": { - "m": { - "n": { - "o": { - "p": true - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "cases": [ - { - "comment": "simple field", - "expression": "a", - "bench": "full" - }, - { - "comment": "simple subexpression", - "expression": "a.b", - "bench": "full" - }, - { - "comment": "deep field selection", - "expression": "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s", - "bench": "full" - }, - { - "comment": "simple or", - "expression": "not_there || a", - "bench": "full" - } - ] - }, - { - "given": { - "a":0,"b":1,"c":2,"d":3,"e":4,"f":5,"g":6,"h":7,"i":8,"j":9,"k":10, - "l":11,"m":12,"n":13,"o":14,"p":15,"q":16,"r":17,"s":18,"t":19,"u":20, - "v":21,"w":22,"x":23,"y":24,"z":25 - }, - "cases": [ - { - "comment": "deep ands", - "expression": "a && b && c && d && e && f && g && h && i && j && k && l && m && n && o && p && q && r && s && t && u && v && w && x && y && z", - "bench": "full" - }, - { - "comment": "deep ors", - "expression": "z || y || x || w || v || u || t || s || r || q || p || o || n || m || l || k || j || i || h || g || f || e || d || c || b || a", - "bench": "full" - }, - { - "comment": "lots of summing", - "expression": "sum(z, y, x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a)", - "bench": "full" - }, - { - "comment": "lots of multi list", - "expression": "[z, y, x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a]", - "bench": "full" - } - ] - }, - { - "given": {}, - "cases": [ - { - "comment": "field 50", - "expression": "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40.j39.j38.j37.j36.j35.j34.j33.j32.j31.j30.j29.j28.j27.j26.j25.j24.j23.j22.j21.j20.j19.j18.j17.j16.j15.j14.j13.j12.j11.j10.j9.j8.j7.j6.j5.j4.j3.j2.j1.j0", - "bench": "parse" - }, - { - "comment": "pipe 50", - "expression": "j49|j48|j47|j46|j45|j44|j43|j42|j41|j40|j39|j38|j37|j36|j35|j34|j33|j32|j31|j30|j29|j28|j27|j26|j25|j24|j23|j22|j21|j20|j19|j18|j17|j16|j15|j14|j13|j12|j11|j10|j9|j8|j7|j6|j5|j4|j3|j2|j1|j0", - "bench": "parse" - }, - { - "comment": "index 50", - "expression": "[49][48][47][46][45][44][43][42][41][40][39][38][37][36][35][34][33][32][31][30][29][28][27][26][25][24][23][22][21][20][19][18][17][16][15][14][13][12][11][10][9][8][7][6][5][4][3][2][1][0]", - "bench": "parse" - }, - { - "comment": "long raw string literal", - "expression": "'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'", - "bench": "parse" - }, - { - "comment": "deep projection 104", - "expression": "a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*]", - "bench": "parse" - }, - { - "comment": "filter projection", - "expression": "foo[bar > baz][qux > baz]", - "bench": "parse" - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/boolean.json b/lib/jmespath/jmespath/tests/compliance/boolean.json deleted file mode 100644 index e3fa196..0000000 --- a/lib/jmespath/jmespath/tests/compliance/boolean.json +++ /dev/null @@ -1,257 +0,0 @@ -[ - { - "given": { - "outer": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - } - }, - "cases": [ - { - "expression": "outer.foo || outer.bar", - "result": "foo" - }, - { - "expression": "outer.foo||outer.bar", - "result": "foo" - }, - { - "expression": "outer.bar || outer.baz", - "result": "bar" - }, - { - "expression": "outer.bar||outer.baz", - "result": "bar" - }, - { - "expression": "outer.bad || outer.foo", - "result": "foo" - }, - { - "expression": "outer.bad||outer.foo", - "result": "foo" - }, - { - "expression": "outer.foo || outer.bad", - "result": "foo" - }, - { - "expression": "outer.foo||outer.bad", - "result": "foo" - }, - { - "expression": "outer.bad || outer.alsobad", - "result": null - }, - { - "expression": "outer.bad||outer.alsobad", - "result": null - } - ] - }, - { - "given": { - "outer": { - "foo": "foo", - "bool": false, - "empty_list": [], - "empty_string": "" - } - }, - "cases": [ - { - "expression": "outer.empty_string || outer.foo", - "result": "foo" - }, - { - "expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo", - "result": "foo" - } - ] - }, - { - "given": { - "True": true, - "False": false, - "Number": 5, - "EmptyList": [], - "Zero": 0 - }, - "cases": [ - { - "expression": "True && False", - "result": false - }, - { - "expression": "False && True", - "result": false - }, - { - "expression": "True && True", - "result": true - }, - { - "expression": "False && False", - "result": false - }, - { - "expression": "True && Number", - "result": 5 - }, - { - "expression": "Number && True", - "result": true - }, - { - "expression": "Number && False", - "result": false - }, - { - "expression": "Number && EmptyList", - "result": [] - }, - { - "expression": "Number && True", - "result": true - }, - { - "expression": "EmptyList && True", - "result": [] - }, - { - "expression": "EmptyList && False", - "result": [] - }, - { - "expression": "True || False", - "result": true - }, - { - "expression": "True || True", - "result": true - }, - { - "expression": "False || True", - "result": true - }, - { - "expression": "False || False", - "result": false - }, - { - "expression": "Number || EmptyList", - "result": 5 - }, - { - "expression": "Number || True", - "result": 5 - }, - { - "expression": "Number || True && False", - "result": 5 - }, - { - "expression": "(Number || True) && False", - "result": false - }, - { - "expression": "Number || (True && False)", - "result": 5 - }, - { - "expression": "!True", - "result": false - }, - { - "expression": "!False", - "result": true - }, - { - "expression": "!Number", - "result": false - }, - { - "expression": "!EmptyList", - "result": true - }, - { - "expression": "True && !False", - "result": true - }, - { - "expression": "True && !EmptyList", - "result": true - }, - { - "expression": "!False && !EmptyList", - "result": true - }, - { - "expression": "!(True && False)", - "result": true - }, - { - "expression": "!Zero", - "result": false - }, - { - "expression": "!!Zero", - "result": true - } - ] - }, - { - "given": { - "one": 1, - "two": 2, - "three": 3 - }, - "cases": [ - { - "expression": "one < two", - "result": true - }, - { - "expression": "one <= two", - "result": true - }, - { - "expression": "one == one", - "result": true - }, - { - "expression": "one == two", - "result": false - }, - { - "expression": "one > two", - "result": false - }, - { - "expression": "one >= two", - "result": false - }, - { - "expression": "one != two", - "result": true - }, - { - "expression": "one < two && three > one", - "result": true - }, - { - "expression": "one < two || three > one", - "result": true - }, - { - "expression": "one < two || three < one", - "result": true - }, - { - "expression": "two < one || three < one", - "result": false - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/current.json b/lib/jmespath/jmespath/tests/compliance/current.json deleted file mode 100644 index 0c26248..0000000 --- a/lib/jmespath/jmespath/tests/compliance/current.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "given": { - "foo": [{"name": "a"}, {"name": "b"}], - "bar": {"baz": "qux"} - }, - "cases": [ - { - "expression": "@", - "result": { - "foo": [{"name": "a"}, {"name": "b"}], - "bar": {"baz": "qux"} - } - }, - { - "expression": "@.bar", - "result": {"baz": "qux"} - }, - { - "expression": "@.foo[0]", - "result": {"name": "a"} - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/escape.json b/lib/jmespath/jmespath/tests/compliance/escape.json deleted file mode 100644 index 4a62d95..0000000 --- a/lib/jmespath/jmespath/tests/compliance/escape.json +++ /dev/null @@ -1,46 +0,0 @@ -[{ - "given": { - "foo.bar": "dot", - "foo bar": "space", - "foo\nbar": "newline", - "foo\"bar": "doublequote", - "c:\\\\windows\\path": "windows", - "/unix/path": "unix", - "\"\"\"": "threequotes", - "bar": {"baz": "qux"} - }, - "cases": [ - { - "expression": "\"foo.bar\"", - "result": "dot" - }, - { - "expression": "\"foo bar\"", - "result": "space" - }, - { - "expression": "\"foo\\nbar\"", - "result": "newline" - }, - { - "expression": "\"foo\\\"bar\"", - "result": "doublequote" - }, - { - "expression": "\"c:\\\\\\\\windows\\\\path\"", - "result": "windows" - }, - { - "expression": "\"/unix/path\"", - "result": "unix" - }, - { - "expression": "\"\\\"\\\"\\\"\"", - "result": "threequotes" - }, - { - "expression": "\"bar\".\"baz\"", - "result": "qux" - } - ] -}] diff --git a/lib/jmespath/jmespath/tests/compliance/filters.json b/lib/jmespath/jmespath/tests/compliance/filters.json deleted file mode 100644 index 5b9f52b..0000000 --- a/lib/jmespath/jmespath/tests/compliance/filters.json +++ /dev/null @@ -1,468 +0,0 @@ -[ - { - "given": {"foo": [{"name": "a"}, {"name": "b"}]}, - "cases": [ - { - "comment": "Matching a literal", - "expression": "foo[?name == 'a']", - "result": [{"name": "a"}] - } - ] - }, - { - "given": {"foo": [0, 1], "bar": [2, 3]}, - "cases": [ - { - "comment": "Matching a literal", - "expression": "*[?[0] == `0`]", - "result": [[], []] - } - ] - }, - { - "given": {"foo": [{"first": "foo", "last": "bar"}, - {"first": "foo", "last": "foo"}, - {"first": "foo", "last": "baz"}]}, - "cases": [ - { - "comment": "Matching an expression", - "expression": "foo[?first == last]", - "result": [{"first": "foo", "last": "foo"}] - }, - { - "comment": "Verify projection created from filter", - "expression": "foo[?first == last].first", - "result": ["foo"] - } - ] - }, - { - "given": {"foo": [{"age": 20}, - {"age": 25}, - {"age": 30}]}, - "cases": [ - { - "comment": "Greater than with a number", - "expression": "foo[?age > `25`]", - "result": [{"age": 30}] - }, - { - "expression": "foo[?age >= `25`]", - "result": [{"age": 25}, {"age": 30}] - }, - { - "comment": "Greater than with a number", - "expression": "foo[?age > `30`]", - "result": [] - }, - { - "comment": "Greater than with a number", - "expression": "foo[?age < `25`]", - "result": [{"age": 20}] - }, - { - "comment": "Greater than with a number", - "expression": "foo[?age <= `25`]", - "result": [{"age": 20}, {"age": 25}] - }, - { - "comment": "Greater than with a number", - "expression": "foo[?age < `20`]", - "result": [] - }, - { - "expression": "foo[?age == `20`]", - "result": [{"age": 20}] - }, - { - "expression": "foo[?age != `20`]", - "result": [{"age": 25}, {"age": 30}] - } - ] - }, - { - "given": {"foo": [{"top": {"name": "a"}}, - {"top": {"name": "b"}}]}, - "cases": [ - { - "comment": "Filter with subexpression", - "expression": "foo[?top.name == 'a']", - "result": [{"top": {"name": "a"}}] - } - ] - }, - { - "given": {"foo": [{"top": {"first": "foo", "last": "bar"}}, - {"top": {"first": "foo", "last": "foo"}}, - {"top": {"first": "foo", "last": "baz"}}]}, - "cases": [ - { - "comment": "Matching an expression", - "expression": "foo[?top.first == top.last]", - "result": [{"top": {"first": "foo", "last": "foo"}}] - }, - { - "comment": "Matching a JSON array", - "expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]", - "result": [{"top": {"first": "foo", "last": "bar"}}] - } - ] - }, - { - "given": {"foo": [ - {"key": true}, - {"key": false}, - {"key": 0}, - {"key": 1}, - {"key": [0]}, - {"key": {"bar": [0]}}, - {"key": null}, - {"key": [1]}, - {"key": {"a":2}} - ]}, - "cases": [ - { - "expression": "foo[?key == `true`]", - "result": [{"key": true}] - }, - { - "expression": "foo[?key == `false`]", - "result": [{"key": false}] - }, - { - "expression": "foo[?key == `0`]", - "result": [{"key": 0}] - }, - { - "expression": "foo[?key == `1`]", - "result": [{"key": 1}] - }, - { - "expression": "foo[?key == `[0]`]", - "result": [{"key": [0]}] - }, - { - "expression": "foo[?key == `{\"bar\": [0]}`]", - "result": [{"key": {"bar": [0]}}] - }, - { - "expression": "foo[?key == `null`]", - "result": [{"key": null}] - }, - { - "expression": "foo[?key == `[1]`]", - "result": [{"key": [1]}] - }, - { - "expression": "foo[?key == `{\"a\":2}`]", - "result": [{"key": {"a":2}}] - }, - { - "expression": "foo[?`true` == key]", - "result": [{"key": true}] - }, - { - "expression": "foo[?`false` == key]", - "result": [{"key": false}] - }, - { - "expression": "foo[?`0` == key]", - "result": [{"key": 0}] - }, - { - "expression": "foo[?`1` == key]", - "result": [{"key": 1}] - }, - { - "expression": "foo[?`[0]` == key]", - "result": [{"key": [0]}] - }, - { - "expression": "foo[?`{\"bar\": [0]}` == key]", - "result": [{"key": {"bar": [0]}}] - }, - { - "expression": "foo[?`null` == key]", - "result": [{"key": null}] - }, - { - "expression": "foo[?`[1]` == key]", - "result": [{"key": [1]}] - }, - { - "expression": "foo[?`{\"a\":2}` == key]", - "result": [{"key": {"a":2}}] - }, - { - "expression": "foo[?key != `true`]", - "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?key != `false`]", - "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?key != `0`]", - "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?key != `1`]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?key != `null`]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?key != `[1]`]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] - }, - { - "expression": "foo[?key != `{\"a\":2}`]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] - }, - { - "expression": "foo[?`true` != key]", - "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?`false` != key]", - "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?`0` != key]", - "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?`1` != key]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?`null` != key]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] - }, - { - "expression": "foo[?`[1]` != key]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] - }, - { - "expression": "foo[?`{\"a\":2}` != key]", - "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, - {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] - } - ] - }, - { - "given": {"reservations": [ - {"instances": [ - {"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, - {"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]}, - "cases": [ - { - "expression": "reservations[].instances[?bar==`1`]", - "result": [[{"foo": 2, "bar": 1}]] - }, - { - "expression": "reservations[*].instances[?bar==`1`]", - "result": [[{"foo": 2, "bar": 1}]] - }, - { - "expression": "reservations[].instances[?bar==`1`][]", - "result": [{"foo": 2, "bar": 1}] - } - ] - }, - { - "given": { - "baz": "other", - "foo": [ - {"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2} - ] - }, - "cases": [ - { - "expression": "foo[?bar==`1`].bar[0]", - "result": [] - } - ] - }, - { - "given": { - "foo": [ - {"a": 1, "b": {"c": "x"}}, - {"a": 1, "b": {"c": "y"}}, - {"a": 1, "b": {"c": "z"}}, - {"a": 2, "b": {"c": "z"}}, - {"a": 1, "baz": 2} - ] - }, - "cases": [ - { - "expression": "foo[?a==`1`].b.c", - "result": ["x", "y", "z"] - } - ] - }, - { - "given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]}, - "cases": [ - { - "comment": "Filter with or expression", - "expression": "foo[?name == 'a' || name == 'b']", - "result": [{"name": "a"}, {"name": "b"}] - }, - { - "expression": "foo[?name == 'a' || name == 'e']", - "result": [{"name": "a"}] - }, - { - "expression": "foo[?name == 'a' || name == 'b' || name == 'c']", - "result": [{"name": "a"}, {"name": "b"}, {"name": "c"}] - } - ] - }, - { - "given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]}, - "cases": [ - { - "comment": "Filter with and expression", - "expression": "foo[?a == `1` && b == `2`]", - "result": [{"a": 1, "b": 2}] - }, - { - "expression": "foo[?a == `1` && b == `4`]", - "result": [] - } - ] - }, - { - "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, - "cases": [ - { - "comment": "Filter with Or and And expressions", - "expression": "foo[?c == `3` || a == `1` && b == `4`]", - "result": [{"a": 1, "b": 2, "c": 3}] - }, - { - "expression": "foo[?b == `2` || a == `3` && b == `4`]", - "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] - }, - { - "expression": "foo[?a == `3` && b == `4` || b == `2`]", - "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] - }, - { - "expression": "foo[?(a == `3` && b == `4`) || b == `2`]", - "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] - }, - { - "expression": "foo[?((a == `3` && b == `4`)) || b == `2`]", - "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] - }, - { - "expression": "foo[?a == `3` && (b == `4` || b == `2`)]", - "result": [{"a": 3, "b": 4}] - }, - { - "expression": "foo[?a == `3` && ((b == `4` || b == `2`))]", - "result": [{"a": 3, "b": 4}] - } - ] - }, - { - "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, - "cases": [ - { - "comment": "Verify precedence of or/and expressions", - "expression": "foo[?a == `1` || b ==`2` && c == `5`]", - "result": [{"a": 1, "b": 2, "c": 3}] - }, - { - "comment": "Parentheses can alter precedence", - "expression": "foo[?(a == `1` || b ==`2`) && c == `5`]", - "result": [] - }, - { - "comment": "Not expressions combined with and/or", - "expression": "foo[?!(a == `1` || b ==`2`)]", - "result": [{"a": 3, "b": 4}] - } - ] - }, - { - "given": { - "foo": [ - {"key": true}, - {"key": false}, - {"key": []}, - {"key": {}}, - {"key": [0]}, - {"key": {"a": "b"}}, - {"key": 0}, - {"key": 1}, - {"key": null}, - {"notkey": true} - ] - }, - "cases": [ - { - "comment": "Unary filter expression", - "expression": "foo[?key]", - "result": [ - {"key": true}, {"key": [0]}, {"key": {"a": "b"}}, - {"key": 0}, {"key": 1} - ] - }, - { - "comment": "Unary not filter expression", - "expression": "foo[?!key]", - "result": [ - {"key": false}, {"key": []}, {"key": {}}, - {"key": null}, {"notkey": true} - ] - }, - { - "comment": "Equality with null RHS", - "expression": "foo[?key == `null`]", - "result": [ - {"key": null}, {"notkey": true} - ] - } - ] - }, - { - "given": { - "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - "cases": [ - { - "comment": "Using @ in a filter expression", - "expression": "foo[?@ < `5`]", - "result": [0, 1, 2, 3, 4] - }, - { - "comment": "Using @ in a filter expression", - "expression": "foo[?`5` > @]", - "result": [0, 1, 2, 3, 4] - }, - { - "comment": "Using @ in a filter expression", - "expression": "foo[?@ == @]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/functions.json b/lib/jmespath/jmespath/tests/compliance/functions.json deleted file mode 100644 index cd4aebb..0000000 --- a/lib/jmespath/jmespath/tests/compliance/functions.json +++ /dev/null @@ -1,825 +0,0 @@ -[{ - "given": - { - "foo": -1, - "zero": 0, - "numbers": [-1, 3, 4, 5], - "array": [-1, 3, 4, 5, "a", "100"], - "strings": ["a", "b", "c"], - "decimals": [1.01, 1.2, -1.5], - "str": "Str", - "false": false, - "empty_list": [], - "empty_hash": {}, - "objects": {"foo": "bar", "bar": "baz"}, - "null_key": null - }, - "cases": [ - { - "expression": "abs(foo)", - "result": 1 - }, - { - "expression": "abs(foo)", - "result": 1 - }, - { - "expression": "abs(str)", - "error": "invalid-type" - }, - { - "expression": "abs(array[1])", - "result": 3 - }, - { - "expression": "abs(array[1])", - "result": 3 - }, - { - "expression": "abs(`false`)", - "error": "invalid-type" - }, - { - "expression": "abs(`-24`)", - "result": 24 - }, - { - "expression": "abs(`-24`)", - "result": 24 - }, - { - "expression": "abs(`1`, `2`)", - "error": "invalid-arity" - }, - { - "expression": "abs()", - "error": "invalid-arity" - }, - { - "expression": "unknown_function(`1`, `2`)", - "error": "unknown-function" - }, - { - "expression": "avg(numbers)", - "result": 2.75 - }, - { - "expression": "avg(array)", - "error": "invalid-type" - }, - { - "expression": "avg('abc')", - "error": "invalid-type" - }, - { - "expression": "avg(foo)", - "error": "invalid-type" - }, - { - "expression": "avg(@)", - "error": "invalid-type" - }, - { - "expression": "avg(strings)", - "error": "invalid-type" - }, - { - "expression": "ceil(`1.2`)", - "result": 2 - }, - { - "expression": "ceil(decimals[0])", - "result": 2 - }, - { - "expression": "ceil(decimals[1])", - "result": 2 - }, - { - "expression": "ceil(decimals[2])", - "result": -1 - }, - { - "expression": "ceil('string')", - "error": "invalid-type" - }, - { - "expression": "contains('abc', 'a')", - "result": true - }, - { - "expression": "contains('abc', 'd')", - "result": false - }, - { - "expression": "contains(`false`, 'd')", - "error": "invalid-type" - }, - { - "expression": "contains(strings, 'a')", - "result": true - }, - { - "expression": "contains(decimals, `1.2`)", - "result": true - }, - { - "expression": "contains(decimals, `false`)", - "result": false - }, - { - "expression": "ends_with(str, 'r')", - "result": true - }, - { - "expression": "ends_with(str, 'tr')", - "result": true - }, - { - "expression": "ends_with(str, 'Str')", - "result": true - }, - { - "expression": "ends_with(str, 'SStr')", - "result": false - }, - { - "expression": "ends_with(str, 'foo')", - "result": false - }, - { - "expression": "ends_with(str, `0`)", - "error": "invalid-type" - }, - { - "expression": "floor(`1.2`)", - "result": 1 - }, - { - "expression": "floor('string')", - "error": "invalid-type" - }, - { - "expression": "floor(decimals[0])", - "result": 1 - }, - { - "expression": "floor(foo)", - "result": -1 - }, - { - "expression": "floor(str)", - "error": "invalid-type" - }, - { - "expression": "length('abc')", - "result": 3 - }, - { - "expression": "length('✓foo')", - "result": 4 - }, - { - "expression": "length('')", - "result": 0 - }, - { - "expression": "length(@)", - "result": 12 - }, - { - "expression": "length(strings[0])", - "result": 1 - }, - { - "expression": "length(str)", - "result": 3 - }, - { - "expression": "length(array)", - "result": 6 - }, - { - "expression": "length(objects)", - "result": 2 - }, - { - "expression": "length(`false`)", - "error": "invalid-type" - }, - { - "expression": "length(foo)", - "error": "invalid-type" - }, - { - "expression": "length(strings[0])", - "result": 1 - }, - { - "expression": "max(numbers)", - "result": 5 - }, - { - "expression": "max(decimals)", - "result": 1.2 - }, - { - "expression": "max(strings)", - "result": "c" - }, - { - "expression": "max(abc)", - "error": "invalid-type" - }, - { - "expression": "max(array)", - "error": "invalid-type" - }, - { - "expression": "max(decimals)", - "result": 1.2 - }, - { - "expression": "max(empty_list)", - "result": null - }, - { - "expression": "merge(`{}`)", - "result": {} - }, - { - "expression": "merge(`{}`, `{}`)", - "result": {} - }, - { - "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)", - "result": {"a": 1, "b": 2} - }, - { - "expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)", - "result": {"a": 2} - }, - { - "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)", - "result": {"a": 2, "b": 2, "c": 3, "d": 4} - }, - { - "expression": "min(numbers)", - "result": -1 - }, - { - "expression": "min(decimals)", - "result": -1.5 - }, - { - "expression": "min(abc)", - "error": "invalid-type" - }, - { - "expression": "min(array)", - "error": "invalid-type" - }, - { - "expression": "min(empty_list)", - "result": null - }, - { - "expression": "min(decimals)", - "result": -1.5 - }, - { - "expression": "min(strings)", - "result": "a" - }, - { - "expression": "type('abc')", - "result": "string" - }, - { - "expression": "type(`1.0`)", - "result": "number" - }, - { - "expression": "type(`2`)", - "result": "number" - }, - { - "expression": "type(`true`)", - "result": "boolean" - }, - { - "expression": "type(`false`)", - "result": "boolean" - }, - { - "expression": "type(`null`)", - "result": "null" - }, - { - "expression": "type(`[0]`)", - "result": "array" - }, - { - "expression": "type(`{\"a\": \"b\"}`)", - "result": "object" - }, - { - "expression": "type(@)", - "result": "object" - }, - { - "expression": "sort(keys(objects))", - "result": ["bar", "foo"] - }, - { - "expression": "keys(foo)", - "error": "invalid-type" - }, - { - "expression": "keys(strings)", - "error": "invalid-type" - }, - { - "expression": "keys(`false`)", - "error": "invalid-type" - }, - { - "expression": "sort(values(objects))", - "result": ["bar", "baz"] - }, - { - "expression": "keys(empty_hash)", - "result": [] - }, - { - "expression": "values(foo)", - "error": "invalid-type" - }, - { - "expression": "join(', ', strings)", - "result": "a, b, c" - }, - { - "expression": "join(', ', strings)", - "result": "a, b, c" - }, - { - "expression": "join(',', `[\"a\", \"b\"]`)", - "result": "a,b" - }, - { - "expression": "join(',', `[\"a\", 0]`)", - "error": "invalid-type" - }, - { - "expression": "join(', ', str)", - "error": "invalid-type" - }, - { - "expression": "join('|', strings)", - "result": "a|b|c" - }, - { - "expression": "join(`2`, strings)", - "error": "invalid-type" - }, - { - "expression": "join('|', decimals)", - "error": "invalid-type" - }, - { - "expression": "join('|', decimals[].to_string(@))", - "result": "1.01|1.2|-1.5" - }, - { - "expression": "join('|', empty_list)", - "result": "" - }, - { - "expression": "reverse(numbers)", - "result": [5, 4, 3, -1] - }, - { - "expression": "reverse(array)", - "result": ["100", "a", 5, 4, 3, -1] - }, - { - "expression": "reverse(`[]`)", - "result": [] - }, - { - "expression": "reverse('')", - "result": "" - }, - { - "expression": "reverse('hello world')", - "result": "dlrow olleh" - }, - { - "expression": "starts_with(str, 'S')", - "result": true - }, - { - "expression": "starts_with(str, 'St')", - "result": true - }, - { - "expression": "starts_with(str, 'Str')", - "result": true - }, - { - "expression": "starts_with(str, 'String')", - "result": false - }, - { - "expression": "starts_with(str, `0`)", - "error": "invalid-type" - }, - { - "expression": "sum(numbers)", - "result": 11 - }, - { - "expression": "sum(decimals)", - "result": 0.71 - }, - { - "expression": "sum(array)", - "error": "invalid-type" - }, - { - "expression": "sum(array[].to_number(@))", - "result": 111 - }, - { - "expression": "sum(`[]`)", - "result": 0 - }, - { - "expression": "to_array('foo')", - "result": ["foo"] - }, - { - "expression": "to_array(`0`)", - "result": [0] - }, - { - "expression": "to_array(objects)", - "result": [{"foo": "bar", "bar": "baz"}] - }, - { - "expression": "to_array(`[1, 2, 3]`)", - "result": [1, 2, 3] - }, - { - "expression": "to_array(false)", - "result": [false] - }, - { - "expression": "to_string('foo')", - "result": "foo" - }, - { - "expression": "to_string(`1.2`)", - "result": "1.2" - }, - { - "expression": "to_string(`[0, 1]`)", - "result": "[0,1]" - }, - { - "expression": "to_number('1.0')", - "result": 1.0 - }, - { - "expression": "to_number('1.1')", - "result": 1.1 - }, - { - "expression": "to_number('4')", - "result": 4 - }, - { - "expression": "to_number('notanumber')", - "result": null - }, - { - "expression": "to_number(`false`)", - "result": null - }, - { - "expression": "to_number(`null`)", - "result": null - }, - { - "expression": "to_number(`[0]`)", - "result": null - }, - { - "expression": "to_number(`{\"foo\": 0}`)", - "result": null - }, - { - "expression": "\"to_string\"(`1.0`)", - "error": "syntax" - }, - { - "expression": "sort(numbers)", - "result": [-1, 3, 4, 5] - }, - { - "expression": "sort(strings)", - "result": ["a", "b", "c"] - }, - { - "expression": "sort(decimals)", - "result": [-1.5, 1.01, 1.2] - }, - { - "expression": "sort(array)", - "error": "invalid-type" - }, - { - "expression": "sort(abc)", - "error": "invalid-type" - }, - { - "expression": "sort(empty_list)", - "result": [] - }, - { - "expression": "sort(@)", - "error": "invalid-type" - }, - { - "expression": "not_null(unknown_key, str)", - "result": "Str" - }, - { - "expression": "not_null(unknown_key, foo.bar, empty_list, str)", - "result": [] - }, - { - "expression": "not_null(unknown_key, null_key, empty_list, str)", - "result": [] - }, - { - "expression": "not_null(all, expressions, are_null)", - "result": null - }, - { - "expression": "not_null()", - "error": "invalid-arity" - }, - { - "comment": "function projection on single arg function", - "expression": "numbers[].to_string(@)", - "result": ["-1", "3", "4", "5"] - }, - { - "comment": "function projection on single arg function", - "expression": "array[].to_number(@)", - "result": [-1, 3, 4, 5, 100] - } - ] -}, { - "given": - { - "foo": [ - {"b": "b", "a": "a"}, - {"c": "c", "b": "b"}, - {"d": "d", "c": "c"}, - {"e": "e", "d": "d"}, - {"f": "f", "e": "e"} - ] - }, - "cases": [ - { - "comment": "function projection on variadic function", - "expression": "foo[].not_null(f, e, d, c, b, a)", - "result": ["b", "c", "d", "e", "f"] - } - ] -}, { - "given": - { - "people": [ - {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, - {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, - {"age": 30, "age_str": "30", "bool": true, "name": "c"}, - {"age": 50, "age_str": "50", "bool": false, "name": "d"}, - {"age": 10, "age_str": "10", "bool": true, "name": 3} - ] - }, - "cases": [ - { - "comment": "sort by field expression", - "expression": "sort_by(people, &age)", - "result": [ - {"age": 10, "age_str": "10", "bool": true, "name": 3}, - {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, - {"age": 30, "age_str": "30", "bool": true, "name": "c"}, - {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, - {"age": 50, "age_str": "50", "bool": false, "name": "d"} - ] - }, - { - "expression": "sort_by(people, &age_str)", - "result": [ - {"age": 10, "age_str": "10", "bool": true, "name": 3}, - {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, - {"age": 30, "age_str": "30", "bool": true, "name": "c"}, - {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, - {"age": 50, "age_str": "50", "bool": false, "name": "d"} - ] - }, - { - "comment": "sort by function expression", - "expression": "sort_by(people, &to_number(age_str))", - "result": [ - {"age": 10, "age_str": "10", "bool": true, "name": 3}, - {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, - {"age": 30, "age_str": "30", "bool": true, "name": "c"}, - {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, - {"age": 50, "age_str": "50", "bool": false, "name": "d"} - ] - }, - { - "comment": "function projection on sort_by function", - "expression": "sort_by(people, &age)[].name", - "result": [3, "a", "c", "b", "d"] - }, - { - "expression": "sort_by(people, &extra)", - "error": "invalid-type" - }, - { - "expression": "sort_by(people, &bool)", - "error": "invalid-type" - }, - { - "expression": "sort_by(people, &name)", - "error": "invalid-type" - }, - { - "expression": "sort_by(people, name)", - "error": "invalid-type" - }, - { - "expression": "sort_by(people, &age)[].extra", - "result": ["foo", "bar"] - }, - { - "expression": "sort_by(`[]`, &age)", - "result": [] - }, - { - "expression": "max_by(people, &age)", - "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} - }, - { - "expression": "max_by(people, &age_str)", - "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} - }, - { - "expression": "max_by(people, &bool)", - "error": "invalid-type" - }, - { - "expression": "max_by(people, &extra)", - "error": "invalid-type" - }, - { - "expression": "max_by(people, &to_number(age_str))", - "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} - }, - { - "expression": "min_by(people, &age)", - "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} - }, - { - "expression": "min_by(people, &age_str)", - "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} - }, - { - "expression": "min_by(people, &bool)", - "error": "invalid-type" - }, - { - "expression": "min_by(people, &extra)", - "error": "invalid-type" - }, - { - "expression": "min_by(people, &to_number(age_str))", - "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} - } - ] -}, { - "given": - { - "people": [ - {"age": 10, "order": "1"}, - {"age": 10, "order": "2"}, - {"age": 10, "order": "3"}, - {"age": 10, "order": "4"}, - {"age": 10, "order": "5"}, - {"age": 10, "order": "6"}, - {"age": 10, "order": "7"}, - {"age": 10, "order": "8"}, - {"age": 10, "order": "9"}, - {"age": 10, "order": "10"}, - {"age": 10, "order": "11"} - ] - }, - "cases": [ - { - "comment": "stable sort order", - "expression": "sort_by(people, &age)", - "result": [ - {"age": 10, "order": "1"}, - {"age": 10, "order": "2"}, - {"age": 10, "order": "3"}, - {"age": 10, "order": "4"}, - {"age": 10, "order": "5"}, - {"age": 10, "order": "6"}, - {"age": 10, "order": "7"}, - {"age": 10, "order": "8"}, - {"age": 10, "order": "9"}, - {"age": 10, "order": "10"}, - {"age": 10, "order": "11"} - ] - } - ] -}, { - "given": - { - "people": [ - {"a": 10, "b": 1, "c": "z"}, - {"a": 10, "b": 2, "c": null}, - {"a": 10, "b": 3}, - {"a": 10, "b": 4, "c": "z"}, - {"a": 10, "b": 5, "c": null}, - {"a": 10, "b": 6}, - {"a": 10, "b": 7, "c": "z"}, - {"a": 10, "b": 8, "c": null}, - {"a": 10, "b": 9} - ], - "empty": [] - }, - "cases": [ - { - "expression": "map(&a, people)", - "result": [10, 10, 10, 10, 10, 10, 10, 10, 10] - }, - { - "expression": "map(&c, people)", - "result": ["z", null, null, "z", null, null, "z", null, null] - }, - { - "expression": "map(&a, badkey)", - "error": "invalid-type" - }, - { - "expression": "map(&foo, empty)", - "result": [] - } - ] -}, { - "given": { - "array": [ - { - "foo": {"bar": "yes1"} - }, - { - "foo": {"bar": "yes2"} - }, - { - "foo1": {"bar": "no"} - } - ]}, - "cases": [ - { - "expression": "map(&foo.bar, array)", - "result": ["yes1", "yes2", null] - }, - { - "expression": "map(&foo1.bar, array)", - "result": [null, null, "no"] - }, - { - "expression": "map(&foo.bar.baz, array)", - "result": [null, null, null] - } - ] -}, { - "given": { - "array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]] - }, - "cases": [ - { - "expression": "map(&[], array)", - "result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]] - } - ] -} -] diff --git a/lib/jmespath/jmespath/tests/compliance/identifiers.json b/lib/jmespath/jmespath/tests/compliance/identifiers.json deleted file mode 100644 index 7998a41..0000000 --- a/lib/jmespath/jmespath/tests/compliance/identifiers.json +++ /dev/null @@ -1,1377 +0,0 @@ -[ - { - "given": { - "__L": true - }, - "cases": [ - { - "expression": "__L", - "result": true - } - ] - }, - { - "given": { - "!\r": true - }, - "cases": [ - { - "expression": "\"!\\r\"", - "result": true - } - ] - }, - { - "given": { - "Y_1623": true - }, - "cases": [ - { - "expression": "Y_1623", - "result": true - } - ] - }, - { - "given": { - "x": true - }, - "cases": [ - { - "expression": "x", - "result": true - } - ] - }, - { - "given": { - "\tF\uCebb": true - }, - "cases": [ - { - "expression": "\"\\tF\\uCebb\"", - "result": true - } - ] - }, - { - "given": { - " \t": true - }, - "cases": [ - { - "expression": "\" \\t\"", - "result": true - } - ] - }, - { - "given": { - " ": true - }, - "cases": [ - { - "expression": "\" \"", - "result": true - } - ] - }, - { - "given": { - "v2": true - }, - "cases": [ - { - "expression": "v2", - "result": true - } - ] - }, - { - "given": { - "\t": true - }, - "cases": [ - { - "expression": "\"\\t\"", - "result": true - } - ] - }, - { - "given": { - "_X": true - }, - "cases": [ - { - "expression": "_X", - "result": true - } - ] - }, - { - "given": { - "\t4\ud9da\udd15": true - }, - "cases": [ - { - "expression": "\"\\t4\\ud9da\\udd15\"", - "result": true - } - ] - }, - { - "given": { - "v24_W": true - }, - "cases": [ - { - "expression": "v24_W", - "result": true - } - ] - }, - { - "given": { - "H": true - }, - "cases": [ - { - "expression": "\"H\"", - "result": true - } - ] - }, - { - "given": { - "\f": true - }, - "cases": [ - { - "expression": "\"\\f\"", - "result": true - } - ] - }, - { - "given": { - "E4": true - }, - "cases": [ - { - "expression": "\"E4\"", - "result": true - } - ] - }, - { - "given": { - "!": true - }, - "cases": [ - { - "expression": "\"!\"", - "result": true - } - ] - }, - { - "given": { - "tM": true - }, - "cases": [ - { - "expression": "tM", - "result": true - } - ] - }, - { - "given": { - " [": true - }, - "cases": [ - { - "expression": "\" [\"", - "result": true - } - ] - }, - { - "given": { - "R!": true - }, - "cases": [ - { - "expression": "\"R!\"", - "result": true - } - ] - }, - { - "given": { - "_6W": true - }, - "cases": [ - { - "expression": "_6W", - "result": true - } - ] - }, - { - "given": { - "\uaBA1\r": true - }, - "cases": [ - { - "expression": "\"\\uaBA1\\r\"", - "result": true - } - ] - }, - { - "given": { - "tL7": true - }, - "cases": [ - { - "expression": "tL7", - "result": true - } - ] - }, - { - "given": { - "<": true - }, - "cases": [ - { - "expression": "\">\"", - "result": true - } - ] - }, - { - "given": { - "hvu": true - }, - "cases": [ - { - "expression": "hvu", - "result": true - } - ] - }, - { - "given": { - "; !": true - }, - "cases": [ - { - "expression": "\"; !\"", - "result": true - } - ] - }, - { - "given": { - "hU": true - }, - "cases": [ - { - "expression": "hU", - "result": true - } - ] - }, - { - "given": { - "!I\n\/": true - }, - "cases": [ - { - "expression": "\"!I\\n\\/\"", - "result": true - } - ] - }, - { - "given": { - "\uEEbF": true - }, - "cases": [ - { - "expression": "\"\\uEEbF\"", - "result": true - } - ] - }, - { - "given": { - "U)\t": true - }, - "cases": [ - { - "expression": "\"U)\\t\"", - "result": true - } - ] - }, - { - "given": { - "fa0_9": true - }, - "cases": [ - { - "expression": "fa0_9", - "result": true - } - ] - }, - { - "given": { - "/": true - }, - "cases": [ - { - "expression": "\"/\"", - "result": true - } - ] - }, - { - "given": { - "Gy": true - }, - "cases": [ - { - "expression": "Gy", - "result": true - } - ] - }, - { - "given": { - "\b": true - }, - "cases": [ - { - "expression": "\"\\b\"", - "result": true - } - ] - }, - { - "given": { - "<": true - }, - "cases": [ - { - "expression": "\"<\"", - "result": true - } - ] - }, - { - "given": { - "\t": true - }, - "cases": [ - { - "expression": "\"\\t\"", - "result": true - } - ] - }, - { - "given": { - "\t&\\\r": true - }, - "cases": [ - { - "expression": "\"\\t&\\\\\\r\"", - "result": true - } - ] - }, - { - "given": { - "#": true - }, - "cases": [ - { - "expression": "\"#\"", - "result": true - } - ] - }, - { - "given": { - "B__": true - }, - "cases": [ - { - "expression": "B__", - "result": true - } - ] - }, - { - "given": { - "\nS \n": true - }, - "cases": [ - { - "expression": "\"\\nS \\n\"", - "result": true - } - ] - }, - { - "given": { - "Bp": true - }, - "cases": [ - { - "expression": "Bp", - "result": true - } - ] - }, - { - "given": { - ",\t;": true - }, - "cases": [ - { - "expression": "\",\\t;\"", - "result": true - } - ] - }, - { - "given": { - "B_q": true - }, - "cases": [ - { - "expression": "B_q", - "result": true - } - ] - }, - { - "given": { - "\/+\t\n\b!Z": true - }, - "cases": [ - { - "expression": "\"\\/+\\t\\n\\b!Z\"", - "result": true - } - ] - }, - { - "given": { - "\udadd\udfc7\\ueFAc": true - }, - "cases": [ - { - "expression": "\"\udadd\udfc7\\\\ueFAc\"", - "result": true - } - ] - }, - { - "given": { - ":\f": true - }, - "cases": [ - { - "expression": "\":\\f\"", - "result": true - } - ] - }, - { - "given": { - "\/": true - }, - "cases": [ - { - "expression": "\"\\/\"", - "result": true - } - ] - }, - { - "given": { - "_BW_6Hg_Gl": true - }, - "cases": [ - { - "expression": "_BW_6Hg_Gl", - "result": true - } - ] - }, - { - "given": { - "\udbcf\udc02": true - }, - "cases": [ - { - "expression": "\"\udbcf\udc02\"", - "result": true - } - ] - }, - { - "given": { - "zs1DC": true - }, - "cases": [ - { - "expression": "zs1DC", - "result": true - } - ] - }, - { - "given": { - "__434": true - }, - "cases": [ - { - "expression": "__434", - "result": true - } - ] - }, - { - "given": { - "\udb94\udd41": true - }, - "cases": [ - { - "expression": "\"\udb94\udd41\"", - "result": true - } - ] - }, - { - "given": { - "Z_5": true - }, - "cases": [ - { - "expression": "Z_5", - "result": true - } - ] - }, - { - "given": { - "z_M_": true - }, - "cases": [ - { - "expression": "z_M_", - "result": true - } - ] - }, - { - "given": { - "YU_2": true - }, - "cases": [ - { - "expression": "YU_2", - "result": true - } - ] - }, - { - "given": { - "_0": true - }, - "cases": [ - { - "expression": "_0", - "result": true - } - ] - }, - { - "given": { - "\b+": true - }, - "cases": [ - { - "expression": "\"\\b+\"", - "result": true - } - ] - }, - { - "given": { - "\"": true - }, - "cases": [ - { - "expression": "\"\\\"\"", - "result": true - } - ] - }, - { - "given": { - "D7": true - }, - "cases": [ - { - "expression": "D7", - "result": true - } - ] - }, - { - "given": { - "_62L": true - }, - "cases": [ - { - "expression": "_62L", - "result": true - } - ] - }, - { - "given": { - "\tK\t": true - }, - "cases": [ - { - "expression": "\"\\tK\\t\"", - "result": true - } - ] - }, - { - "given": { - "\n\\\f": true - }, - "cases": [ - { - "expression": "\"\\n\\\\\\f\"", - "result": true - } - ] - }, - { - "given": { - "I_": true - }, - "cases": [ - { - "expression": "I_", - "result": true - } - ] - }, - { - "given": { - "W_a0_": true - }, - "cases": [ - { - "expression": "W_a0_", - "result": true - } - ] - }, - { - "given": { - "BQ": true - }, - "cases": [ - { - "expression": "BQ", - "result": true - } - ] - }, - { - "given": { - "\tX$\uABBb": true - }, - "cases": [ - { - "expression": "\"\\tX$\\uABBb\"", - "result": true - } - ] - }, - { - "given": { - "Z9": true - }, - "cases": [ - { - "expression": "Z9", - "result": true - } - ] - }, - { - "given": { - "\b%\"\uda38\udd0f": true - }, - "cases": [ - { - "expression": "\"\\b%\\\"\uda38\udd0f\"", - "result": true - } - ] - }, - { - "given": { - "_F": true - }, - "cases": [ - { - "expression": "_F", - "result": true - } - ] - }, - { - "given": { - "!,": true - }, - "cases": [ - { - "expression": "\"!,\"", - "result": true - } - ] - }, - { - "given": { - "\"!": true - }, - "cases": [ - { - "expression": "\"\\\"!\"", - "result": true - } - ] - }, - { - "given": { - "Hh": true - }, - "cases": [ - { - "expression": "Hh", - "result": true - } - ] - }, - { - "given": { - "&": true - }, - "cases": [ - { - "expression": "\"&\"", - "result": true - } - ] - }, - { - "given": { - "9\r\\R": true - }, - "cases": [ - { - "expression": "\"9\\r\\\\R\"", - "result": true - } - ] - }, - { - "given": { - "M_k": true - }, - "cases": [ - { - "expression": "M_k", - "result": true - } - ] - }, - { - "given": { - "!\b\n\udb06\ude52\"\"": true - }, - "cases": [ - { - "expression": "\"!\\b\\n\udb06\ude52\\\"\\\"\"", - "result": true - } - ] - }, - { - "given": { - "6": true - }, - "cases": [ - { - "expression": "\"6\"", - "result": true - } - ] - }, - { - "given": { - "_7": true - }, - "cases": [ - { - "expression": "_7", - "result": true - } - ] - }, - { - "given": { - "0": true - }, - "cases": [ - { - "expression": "\"0\"", - "result": true - } - ] - }, - { - "given": { - "\\8\\": true - }, - "cases": [ - { - "expression": "\"\\\\8\\\\\"", - "result": true - } - ] - }, - { - "given": { - "b7eo": true - }, - "cases": [ - { - "expression": "b7eo", - "result": true - } - ] - }, - { - "given": { - "xIUo9": true - }, - "cases": [ - { - "expression": "xIUo9", - "result": true - } - ] - }, - { - "given": { - "5": true - }, - "cases": [ - { - "expression": "\"5\"", - "result": true - } - ] - }, - { - "given": { - "?": true - }, - "cases": [ - { - "expression": "\"?\"", - "result": true - } - ] - }, - { - "given": { - "sU": true - }, - "cases": [ - { - "expression": "sU", - "result": true - } - ] - }, - { - "given": { - "VH2&H\\\/": true - }, - "cases": [ - { - "expression": "\"VH2&H\\\\\\/\"", - "result": true - } - ] - }, - { - "given": { - "_C": true - }, - "cases": [ - { - "expression": "_C", - "result": true - } - ] - }, - { - "given": { - "_": true - }, - "cases": [ - { - "expression": "_", - "result": true - } - ] - }, - { - "given": { - "<\t": true - }, - "cases": [ - { - "expression": "\"<\\t\"", - "result": true - } - ] - }, - { - "given": { - "\uD834\uDD1E": true - }, - "cases": [ - { - "expression": "\"\\uD834\\uDD1E\"", - "result": true - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/indices.json b/lib/jmespath/jmespath/tests/compliance/indices.json deleted file mode 100644 index aa03b35..0000000 --- a/lib/jmespath/jmespath/tests/compliance/indices.json +++ /dev/null @@ -1,346 +0,0 @@ -[{ - "given": - {"foo": {"bar": ["zero", "one", "two"]}}, - "cases": [ - { - "expression": "foo.bar[0]", - "result": "zero" - }, - { - "expression": "foo.bar[1]", - "result": "one" - }, - { - "expression": "foo.bar[2]", - "result": "two" - }, - { - "expression": "foo.bar[3]", - "result": null - }, - { - "expression": "foo.bar[-1]", - "result": "two" - }, - { - "expression": "foo.bar[-2]", - "result": "one" - }, - { - "expression": "foo.bar[-3]", - "result": "zero" - }, - { - "expression": "foo.bar[-4]", - "result": null - } - ] -}, -{ - "given": - {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, - "cases": [ - { - "expression": "foo.bar", - "result": null - }, - { - "expression": "foo[0].bar", - "result": "one" - }, - { - "expression": "foo[1].bar", - "result": "two" - }, - { - "expression": "foo[2].bar", - "result": "three" - }, - { - "expression": "foo[3].notbar", - "result": "four" - }, - { - "expression": "foo[3].bar", - "result": null - }, - { - "expression": "foo[0]", - "result": {"bar": "one"} - }, - { - "expression": "foo[1]", - "result": {"bar": "two"} - }, - { - "expression": "foo[2]", - "result": {"bar": "three"} - }, - { - "expression": "foo[3]", - "result": {"notbar": "four"} - }, - { - "expression": "foo[4]", - "result": null - } - ] -}, -{ - "given": [ - "one", "two", "three" - ], - "cases": [ - { - "expression": "[0]", - "result": "one" - }, - { - "expression": "[1]", - "result": "two" - }, - { - "expression": "[2]", - "result": "three" - }, - { - "expression": "[-1]", - "result": "three" - }, - { - "expression": "[-2]", - "result": "two" - }, - { - "expression": "[-3]", - "result": "one" - } - ] -}, -{ - "given": {"reservations": [ - {"instances": [{"foo": 1}, {"foo": 2}]} - ]}, - "cases": [ - { - "expression": "reservations[].instances[].foo", - "result": [1, 2] - }, - { - "expression": "reservations[].instances[].bar", - "result": [] - }, - { - "expression": "reservations[].notinstances[].foo", - "result": [] - }, - { - "expression": "reservations[].notinstances[].foo", - "result": [] - } - ] -}, -{ - "given": {"reservations": [{ - "instances": [ - {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, - {"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, - {"foo": "bar"}, - {"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]}, - {"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]}, - {"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]}, - {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} - ], - "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} - }, { - "instances": [ - {"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, - {"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, - {"c": "bar"}, - {"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]}, - {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} - ], - "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} - } - ]}, - "cases": [ - { - "expression": "reservations[].instances[].foo[].bar", - "result": [1, 2, 4, 5, 6, 8] - }, - { - "expression": "reservations[].instances[].foo[].baz", - "result": [] - }, - { - "expression": "reservations[].instances[].notfoo[].bar", - "result": [20, 21, 22, 23, 24, 25] - }, - { - "expression": "reservations[].instances[].notfoo[].notbar", - "result": [[7], [7]] - }, - { - "expression": "reservations[].notinstances[].foo", - "result": [] - }, - { - "expression": "reservations[].instances[].foo[].notbar", - "result": [3, [7]] - }, - { - "expression": "reservations[].instances[].bar[].baz", - "result": [[1], [2], [3], [4]] - }, - { - "expression": "reservations[].instances[].baz[].baz", - "result": [[1, 2], [], [], [3, 4]] - }, - { - "expression": "reservations[].instances[].qux[].baz", - "result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []] - }, - { - "expression": "reservations[].instances[].qux[].baz[]", - "result": [1, 2, 3, 4, 1, 2, 3, 4] - } - ] -}, -{ - "given": { - "foo": [ - [["one", "two"], ["three", "four"]], - [["five", "six"], ["seven", "eight"]], - [["nine"], ["ten"]] - ] - }, - "cases": [ - { - "expression": "foo[]", - "result": [["one", "two"], ["three", "four"], ["five", "six"], - ["seven", "eight"], ["nine"], ["ten"]] - }, - { - "expression": "foo[][0]", - "result": ["one", "three", "five", "seven", "nine", "ten"] - }, - { - "expression": "foo[][1]", - "result": ["two", "four", "six", "eight"] - }, - { - "expression": "foo[][0][0]", - "result": [] - }, - { - "expression": "foo[][2][2]", - "result": [] - }, - { - "expression": "foo[][0][0][100]", - "result": [] - } - ] -}, -{ - "given": { - "foo": [{ - "bar": [ - { - "qux": 2, - "baz": 1 - }, - { - "qux": 4, - "baz": 3 - } - ] - }, - { - "bar": [ - { - "qux": 6, - "baz": 5 - }, - { - "qux": 8, - "baz": 7 - } - ] - } - ] - }, - "cases": [ - { - "expression": "foo", - "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, - {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] - }, - { - "expression": "foo[]", - "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, - {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] - }, - { - "expression": "foo[].bar", - "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], - [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] - }, - { - "expression": "foo[].bar[]", - "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, - {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] - }, - { - "expression": "foo[].bar[].baz", - "result": [1, 3, 5, 7] - } - ] -}, -{ - "given": { - "string": "string", - "hash": {"foo": "bar", "bar": "baz"}, - "number": 23, - "nullvalue": null - }, - "cases": [ - { - "expression": "string[]", - "result": null - }, - { - "expression": "hash[]", - "result": null - }, - { - "expression": "number[]", - "result": null - }, - { - "expression": "nullvalue[]", - "result": null - }, - { - "expression": "string[].foo", - "result": null - }, - { - "expression": "hash[].foo", - "result": null - }, - { - "expression": "number[].foo", - "result": null - }, - { - "expression": "nullvalue[].foo", - "result": null - }, - { - "expression": "nullvalue[].foo[].bar", - "result": null - } - ] -} -] diff --git a/lib/jmespath/jmespath/tests/compliance/literal.json b/lib/jmespath/jmespath/tests/compliance/literal.json deleted file mode 100644 index b5ddbed..0000000 --- a/lib/jmespath/jmespath/tests/compliance/literal.json +++ /dev/null @@ -1,200 +0,0 @@ -[ - { - "given": { - "foo": [{"name": "a"}, {"name": "b"}], - "bar": {"baz": "qux"} - }, - "cases": [ - { - "expression": "`\"foo\"`", - "result": "foo" - }, - { - "comment": "Interpret escaped unicode.", - "expression": "`\"\\u03a6\"`", - "result": "Φ" - }, - { - "expression": "`\"✓\"`", - "result": "✓" - }, - { - "expression": "`[1, 2, 3]`", - "result": [1, 2, 3] - }, - { - "expression": "`{\"a\": \"b\"}`", - "result": {"a": "b"} - }, - { - "expression": "`true`", - "result": true - }, - { - "expression": "`false`", - "result": false - }, - { - "expression": "`null`", - "result": null - }, - { - "expression": "`0`", - "result": 0 - }, - { - "expression": "`1`", - "result": 1 - }, - { - "expression": "`2`", - "result": 2 - }, - { - "expression": "`3`", - "result": 3 - }, - { - "expression": "`4`", - "result": 4 - }, - { - "expression": "`5`", - "result": 5 - }, - { - "expression": "`6`", - "result": 6 - }, - { - "expression": "`7`", - "result": 7 - }, - { - "expression": "`8`", - "result": 8 - }, - { - "expression": "`9`", - "result": 9 - }, - { - "comment": "Escaping a backtick in quotes", - "expression": "`\"foo\\`bar\"`", - "result": "foo`bar" - }, - { - "comment": "Double quote in literal", - "expression": "`\"foo\\\"bar\"`", - "result": "foo\"bar" - }, - { - "expression": "`\"1\\`\"`", - "result": "1`" - }, - { - "comment": "Multiple literal expressions with escapes", - "expression": "`\"\\\\\"`.{a:`\"b\"`}", - "result": {"a": "b"} - }, - { - "comment": "literal . identifier", - "expression": "`{\"a\": \"b\"}`.a", - "result": "b" - }, - { - "comment": "literal . identifier . identifier", - "expression": "`{\"a\": {\"b\": \"c\"}}`.a.b", - "result": "c" - }, - { - "comment": "literal . identifier bracket-expr", - "expression": "`[0, 1, 2]`[1]", - "result": 1 - } - ] - }, - { - "comment": "Literals", - "given": {"type": "object"}, - "cases": [ - { - "comment": "Literal with leading whitespace", - "expression": "` {\"foo\": true}`", - "result": {"foo": true} - }, - { - "comment": "Literal with trailing whitespace", - "expression": "`{\"foo\": true} `", - "result": {"foo": true} - }, - { - "comment": "Literal on RHS of subexpr not allowed", - "expression": "foo.`\"bar\"`", - "error": "syntax" - } - ] - }, - { - "comment": "Raw String Literals", - "given": {}, - "cases": [ - { - "expression": "'foo'", - "result": "foo" - }, - { - "expression": "' foo '", - "result": " foo " - }, - { - "expression": "'0'", - "result": "0" - }, - { - "expression": "'newline\n'", - "result": "newline\n" - }, - { - "expression": "'\n'", - "result": "\n" - }, - { - "expression": "'✓'", - "result": "✓" - }, - { - "expression": "'ð„ž'", - "result": "ð„ž" - }, - { - "expression": "' [foo] '", - "result": " [foo] " - }, - { - "expression": "'[foo]'", - "result": "[foo]" - }, - { - "comment": "Do not interpret escaped unicode.", - "expression": "'\\u03a6'", - "result": "\\u03a6" - }, - { - "comment": "Can escape the single quote", - "expression": "'foo\\'bar'", - "result": "foo'bar" - }, - { - "comment": "Backslash not followed by single quote is treated as any other character", - "expression": "'\\z'", - "result": "\\z" - }, - { - "comment": "Backslash not followed by single quote is treated as any other character", - "expression": "'\\\\'", - "result": "\\\\" - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/multiselect.json b/lib/jmespath/jmespath/tests/compliance/multiselect.json deleted file mode 100644 index 4f46482..0000000 --- a/lib/jmespath/jmespath/tests/compliance/multiselect.json +++ /dev/null @@ -1,398 +0,0 @@ -[{ - "given": { - "foo": { - "bar": "bar", - "baz": "baz", - "qux": "qux", - "nested": { - "one": { - "a": "first", - "b": "second", - "c": "third" - }, - "two": { - "a": "first", - "b": "second", - "c": "third" - }, - "three": { - "a": "first", - "b": "second", - "c": {"inner": "third"} - } - } - }, - "bar": 1, - "baz": 2, - "qux\"": 3 - }, - "cases": [ - { - "expression": "foo.{bar: bar}", - "result": {"bar": "bar"} - }, - { - "expression": "foo.{\"bar\": bar}", - "result": {"bar": "bar"} - }, - { - "expression": "foo.{\"foo.bar\": bar}", - "result": {"foo.bar": "bar"} - }, - { - "expression": "foo.{bar: bar, baz: baz}", - "result": {"bar": "bar", "baz": "baz"} - }, - { - "expression": "foo.{\"bar\": bar, \"baz\": baz}", - "result": {"bar": "bar", "baz": "baz"} - }, - { - "expression": "{\"baz\": baz, \"qux\\\"\": \"qux\\\"\"}", - "result": {"baz": 2, "qux\"": 3} - }, - { - "expression": "foo.{bar:bar,baz:baz}", - "result": {"bar": "bar", "baz": "baz"} - }, - { - "expression": "foo.{bar: bar,qux: qux}", - "result": {"bar": "bar", "qux": "qux"} - }, - { - "expression": "foo.{bar: bar, noexist: noexist}", - "result": {"bar": "bar", "noexist": null} - }, - { - "expression": "foo.{noexist: noexist, alsonoexist: alsonoexist}", - "result": {"noexist": null, "alsonoexist": null} - }, - { - "expression": "foo.badkey.{nokey: nokey, alsonokey: alsonokey}", - "result": null - }, - { - "expression": "foo.nested.*.{a: a,b: b}", - "result": [{"a": "first", "b": "second"}, - {"a": "first", "b": "second"}, - {"a": "first", "b": "second"}] - }, - { - "expression": "foo.nested.three.{a: a, cinner: c.inner}", - "result": {"a": "first", "cinner": "third"} - }, - { - "expression": "foo.nested.three.{a: a, c: c.inner.bad.key}", - "result": {"a": "first", "c": null} - }, - { - "expression": "foo.{a: nested.one.a, b: nested.two.b}", - "result": {"a": "first", "b": "second"} - }, - { - "expression": "{bar: bar, baz: baz}", - "result": {"bar": 1, "baz": 2} - }, - { - "expression": "{bar: bar}", - "result": {"bar": 1} - }, - { - "expression": "{otherkey: bar}", - "result": {"otherkey": 1} - }, - { - "expression": "{no: no, exist: exist}", - "result": {"no": null, "exist": null} - }, - { - "expression": "foo.[bar]", - "result": ["bar"] - }, - { - "expression": "foo.[bar,baz]", - "result": ["bar", "baz"] - }, - { - "expression": "foo.[bar,qux]", - "result": ["bar", "qux"] - }, - { - "expression": "foo.[bar,noexist]", - "result": ["bar", null] - }, - { - "expression": "foo.[noexist,alsonoexist]", - "result": [null, null] - } - ] -}, { - "given": { - "foo": {"bar": 1, "baz": [2, 3, 4]} - }, - "cases": [ - { - "expression": "foo.{bar:bar,baz:baz}", - "result": {"bar": 1, "baz": [2, 3, 4]} - }, - { - "expression": "foo.[bar,baz[0]]", - "result": [1, 2] - }, - { - "expression": "foo.[bar,baz[1]]", - "result": [1, 3] - }, - { - "expression": "foo.[bar,baz[2]]", - "result": [1, 4] - }, - { - "expression": "foo.[bar,baz[3]]", - "result": [1, null] - }, - { - "expression": "foo.[bar[0],baz[3]]", - "result": [null, null] - } - ] -}, { - "given": { - "foo": {"bar": 1, "baz": 2} - }, - "cases": [ - { - "expression": "foo.{bar: bar, baz: baz}", - "result": {"bar": 1, "baz": 2} - }, - { - "expression": "foo.[bar,baz]", - "result": [1, 2] - } - ] -}, { - "given": { - "foo": { - "bar": {"baz": [{"common": "first", "one": 1}, - {"common": "second", "two": 2}]}, - "ignoreme": 1, - "includeme": true - } - }, - "cases": [ - { - "expression": "foo.{bar: bar.baz[1],includeme: includeme}", - "result": {"bar": {"common": "second", "two": 2}, "includeme": true} - }, - { - "expression": "foo.{\"bar.baz.two\": bar.baz[1].two, includeme: includeme}", - "result": {"bar.baz.two": 2, "includeme": true} - }, - { - "expression": "foo.[includeme, bar.baz[*].common]", - "result": [true, ["first", "second"]] - }, - { - "expression": "foo.[includeme, bar.baz[*].none]", - "result": [true, []] - }, - { - "expression": "foo.[includeme, bar.baz[].common]", - "result": [true, ["first", "second"]] - } - ] -}, { - "given": { - "reservations": [{ - "instances": [ - {"id": "id1", - "name": "first"}, - {"id": "id2", - "name": "second"} - ]}, { - "instances": [ - {"id": "id3", - "name": "third"}, - {"id": "id4", - "name": "fourth"} - ]} - ]}, - "cases": [ - { - "expression": "reservations[*].instances[*].{id: id, name: name}", - "result": [[{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}], - [{"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}]] - }, - { - "expression": "reservations[].instances[].{id: id, name: name}", - "result": [{"id": "id1", "name": "first"}, - {"id": "id2", "name": "second"}, - {"id": "id3", "name": "third"}, - {"id": "id4", "name": "fourth"}] - }, - { - "expression": "reservations[].instances[].[id, name]", - "result": [["id1", "first"], - ["id2", "second"], - ["id3", "third"], - ["id4", "fourth"]] - } - ] -}, -{ - "given": { - "foo": [{ - "bar": [ - { - "qux": 2, - "baz": 1 - }, - { - "qux": 4, - "baz": 3 - } - ] - }, - { - "bar": [ - { - "qux": 6, - "baz": 5 - }, - { - "qux": 8, - "baz": 7 - } - ] - } - ] - }, - "cases": [ - { - "expression": "foo", - "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, - {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] - }, - { - "expression": "foo[]", - "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, - {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] - }, - { - "expression": "foo[].bar", - "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], - [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] - }, - { - "expression": "foo[].bar[]", - "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, - {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] - }, - { - "expression": "foo[].bar[].[baz, qux]", - "result": [[1, 2], [3, 4], [5, 6], [7, 8]] - }, - { - "expression": "foo[].bar[].[baz]", - "result": [[1], [3], [5], [7]] - }, - { - "expression": "foo[].bar[].[baz, qux][]", - "result": [1, 2, 3, 4, 5, 6, 7, 8] - } - ] -}, -{ - "given": { - "foo": { - "baz": [ - { - "bar": "abc" - }, { - "bar": "def" - } - ], - "qux": ["zero"] - } - }, - "cases": [ - { - "expression": "foo.[baz[*].bar, qux[0]]", - "result": [["abc", "def"], "zero"] - } - ] -}, -{ - "given": { - "foo": { - "baz": [ - { - "bar": "a", - "bam": "b", - "boo": "c" - }, { - "bar": "d", - "bam": "e", - "boo": "f" - } - ], - "qux": ["zero"] - } - }, - "cases": [ - { - "expression": "foo.[baz[*].[bar, boo], qux[0]]", - "result": [[["a", "c" ], ["d", "f" ]], "zero"] - } - ] -}, -{ - "given": { - "foo": { - "baz": [ - { - "bar": "a", - "bam": "b", - "boo": "c" - }, { - "bar": "d", - "bam": "e", - "boo": "f" - } - ], - "qux": ["zero"] - } - }, - "cases": [ - { - "expression": "foo.[baz[*].not_there || baz[*].bar, qux[0]]", - "result": [["a", "d"], "zero"] - } - ] -}, -{ - "given": {"type": "object"}, - "cases": [ - { - "comment": "Nested multiselect", - "expression": "[[*],*]", - "result": [null, ["object"]] - } - ] -}, -{ - "given": [], - "cases": [ - { - "comment": "Nested multiselect", - "expression": "[[*]]", - "result": [[]] - }, - { - "comment": "Select on null", - "expression": "missing.{foo: bar}", - "result": null - } - ] -} -] diff --git a/lib/jmespath/jmespath/tests/compliance/pipe.json b/lib/jmespath/jmespath/tests/compliance/pipe.json deleted file mode 100644 index b10c0a4..0000000 --- a/lib/jmespath/jmespath/tests/compliance/pipe.json +++ /dev/null @@ -1,131 +0,0 @@ -[{ - "given": { - "foo": { - "bar": { - "baz": "subkey" - }, - "other": { - "baz": "subkey" - }, - "other2": { - "baz": "subkey" - }, - "other3": { - "notbaz": ["a", "b", "c"] - }, - "other4": { - "notbaz": ["a", "b", "c"] - } - } - }, - "cases": [ - { - "expression": "foo.*.baz | [0]", - "result": "subkey" - }, - { - "expression": "foo.*.baz | [1]", - "result": "subkey" - }, - { - "expression": "foo.*.baz | [2]", - "result": "subkey" - }, - { - "expression": "foo.bar.* | [0]", - "result": "subkey" - }, - { - "expression": "foo.*.notbaz | [*]", - "result": [["a", "b", "c"], ["a", "b", "c"]] - }, - { - "expression": "{\"a\": foo.bar, \"b\": foo.other} | *.baz", - "result": ["subkey", "subkey"] - } - ] -}, { - "given": { - "foo": { - "bar": { - "baz": "one" - }, - "other": { - "baz": "two" - }, - "other2": { - "baz": "three" - }, - "other3": { - "notbaz": ["a", "b", "c"] - }, - "other4": { - "notbaz": ["d", "e", "f"] - } - } - }, - "cases": [ - { - "expression": "foo | bar", - "result": {"baz": "one"} - }, - { - "expression": "foo | bar | baz", - "result": "one" - }, - { - "expression": "foo|bar| baz", - "result": "one" - }, - { - "expression": "not_there | [0]", - "result": null - }, - { - "expression": "not_there | [0]", - "result": null - }, - { - "expression": "[foo.bar, foo.other] | [0]", - "result": {"baz": "one"} - }, - { - "expression": "{\"a\": foo.bar, \"b\": foo.other} | a", - "result": {"baz": "one"} - }, - { - "expression": "{\"a\": foo.bar, \"b\": foo.other} | b", - "result": {"baz": "two"} - }, - { - "expression": "foo.bam || foo.bar | baz", - "result": "one" - }, - { - "expression": "foo | not_there || bar", - "result": {"baz": "one"} - } - ] -}, { - "given": { - "foo": [{ - "bar": [{ - "baz": "one" - }, { - "baz": "two" - }] - }, { - "bar": [{ - "baz": "three" - }, { - "baz": "four" - }] - }] - }, - "cases": [ - { - "expression": "foo[*].bar[*] | [0][0]", - "result": {"baz": "one"} - } - ] -}] diff --git a/lib/jmespath/jmespath/tests/compliance/slice.json b/lib/jmespath/jmespath/tests/compliance/slice.json deleted file mode 100644 index 3594772..0000000 --- a/lib/jmespath/jmespath/tests/compliance/slice.json +++ /dev/null @@ -1,187 +0,0 @@ -[{ - "given": { - "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "bar": { - "baz": 1 - } - }, - "cases": [ - { - "expression": "bar[0:10]", - "result": null - }, - { - "expression": "foo[0:10:1]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[0:10]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[0:10:]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[0::1]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[0::]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[0:]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[:10:1]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[::1]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[:10:]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[::]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[:]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[1:9]", - "result": [1, 2, 3, 4, 5, 6, 7, 8] - }, - { - "expression": "foo[0:10:2]", - "result": [0, 2, 4, 6, 8] - }, - { - "expression": "foo[5:]", - "result": [5, 6, 7, 8, 9] - }, - { - "expression": "foo[5::2]", - "result": [5, 7, 9] - }, - { - "expression": "foo[::2]", - "result": [0, 2, 4, 6, 8] - }, - { - "expression": "foo[::-1]", - "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - }, - { - "expression": "foo[1::2]", - "result": [1, 3, 5, 7, 9] - }, - { - "expression": "foo[10:0:-1]", - "result": [9, 8, 7, 6, 5, 4, 3, 2, 1] - }, - { - "expression": "foo[10:5:-1]", - "result": [9, 8, 7, 6] - }, - { - "expression": "foo[8:2:-2]", - "result": [8, 6, 4] - }, - { - "expression": "foo[0:20]", - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, - { - "expression": "foo[10:-20:-1]", - "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - }, - { - "expression": "foo[10:-20]", - "result": [] - }, - { - "expression": "foo[-4:-1]", - "result": [6, 7, 8] - }, - { - "expression": "foo[:-5:-1]", - "result": [9, 8, 7, 6] - }, - { - "expression": "foo[8:2:0]", - "error": "invalid-value" - }, - { - "expression": "foo[8:2:0:1]", - "error": "syntax" - }, - { - "expression": "foo[8:2&]", - "error": "syntax" - }, - { - "expression": "foo[2:a:3]", - "error": "syntax" - } - ] -}, { - "given": { - "foo": [{"a": 1}, {"a": 2}, {"a": 3}], - "bar": [{"a": {"b": 1}}, {"a": {"b": 2}}, - {"a": {"b": 3}}], - "baz": 50 - }, - "cases": [ - { - "expression": "foo[:2].a", - "result": [1, 2] - }, - { - "expression": "foo[:2].b", - "result": [] - }, - { - "expression": "foo[:2].a.b", - "result": [] - }, - { - "expression": "bar[::-1].a.b", - "result": [3, 2, 1] - }, - { - "expression": "bar[:2].a.b", - "result": [1, 2] - }, - { - "expression": "baz[:2].a", - "result": null - } - ] -}, { - "given": [{"a": 1}, {"a": 2}, {"a": 3}], - "cases": [ - { - "expression": "[:]", - "result": [{"a": 1}, {"a": 2}, {"a": 3}] - }, - { - "expression": "[:2].a", - "result": [1, 2] - }, - { - "expression": "[::-1].a", - "result": [3, 2, 1] - }, - { - "expression": "[:2].b", - "result": [] - } - ] -}] diff --git a/lib/jmespath/jmespath/tests/compliance/syntax.json b/lib/jmespath/jmespath/tests/compliance/syntax.json deleted file mode 100644 index 804c6b2..0000000 --- a/lib/jmespath/jmespath/tests/compliance/syntax.json +++ /dev/null @@ -1,670 +0,0 @@ -[{ - "comment": "Dot syntax", - "given": {"type": "object"}, - "cases": [ - { - "expression": "foo.bar", - "result": null - }, - { - "expression": "foo.1", - "error": "syntax" - }, - { - "expression": "foo.-11", - "error": "syntax" - }, - { - "expression": "foo.", - "error": "syntax" - }, - { - "expression": ".foo", - "error": "syntax" - }, - { - "expression": "foo..bar", - "error": "syntax" - }, - { - "expression": "foo.bar.", - "error": "syntax" - }, - { - "expression": "foo[.]", - "error": "syntax" - } - ] -}, - { - "comment": "Simple token errors", - "given": {"type": "object"}, - "cases": [ - { - "expression": ".", - "error": "syntax" - }, - { - "expression": ":", - "error": "syntax" - }, - { - "expression": ",", - "error": "syntax" - }, - { - "expression": "]", - "error": "syntax" - }, - { - "expression": "[", - "error": "syntax" - }, - { - "expression": "}", - "error": "syntax" - }, - { - "expression": "{", - "error": "syntax" - }, - { - "expression": ")", - "error": "syntax" - }, - { - "expression": "(", - "error": "syntax" - }, - { - "expression": "((&", - "error": "syntax" - }, - { - "expression": "a[", - "error": "syntax" - }, - { - "expression": "a]", - "error": "syntax" - }, - { - "expression": "a][", - "error": "syntax" - }, - { - "expression": "!", - "error": "syntax" - } - ] - }, - { - "comment": "Boolean syntax errors", - "given": {"type": "object"}, - "cases": [ - { - "expression": "![!(!", - "error": "syntax" - } - ] - }, - { - "comment": "Paren syntax errors", - "given": {}, - "cases": [ - { - "comment": "missing closing paren", - "expression": "(@", - "error": "syntax" - } - ] - }, - { - "comment": "Function syntax errors", - "given": {}, - "cases": [ - { - "comment": "invalid start of function", - "expression": "@(foo)", - "error": "syntax" - }, - { - "comment": "function names cannot be quoted", - "expression": "\"foo\"(bar)", - "error": "syntax" - } - ] - }, - { - "comment": "Wildcard syntax", - "given": {"type": "object"}, - "cases": [ - { - "expression": "*", - "result": ["object"] - }, - { - "expression": "*.*", - "result": [] - }, - { - "expression": "*.foo", - "result": [] - }, - { - "expression": "*[0]", - "result": [] - }, - { - "expression": ".*", - "error": "syntax" - }, - { - "expression": "*foo", - "error": "syntax" - }, - { - "expression": "*0", - "error": "syntax" - }, - { - "expression": "foo[*]bar", - "error": "syntax" - }, - { - "expression": "foo[*]*", - "error": "syntax" - } - ] - }, - { - "comment": "Flatten syntax", - "given": {"type": "object"}, - "cases": [ - { - "expression": "[]", - "result": null - } - ] - }, - { - "comment": "Simple bracket syntax", - "given": {"type": "object"}, - "cases": [ - { - "expression": "[0]", - "result": null - }, - { - "expression": "[*]", - "result": null - }, - { - "expression": "*.[0]", - "error": "syntax" - }, - { - "expression": "*.[\"0\"]", - "result": [[null]] - }, - { - "expression": "[*].bar", - "result": null - }, - { - "expression": "[*][0]", - "result": null - }, - { - "expression": "foo[#]", - "error": "syntax" - }, - { - "comment": "missing rbracket for led wildcard index", - "expression": "led[*", - "error": "syntax" - } - ] - }, - { - "comment": "slice syntax", - "given": {}, - "cases": [ - { - "comment": "slice expected colon or rbracket", - "expression": "[:@]", - "error": "syntax" - }, - { - "comment": "slice has too many colons", - "expression": "[:::]", - "error": "syntax" - }, - { - "comment": "slice expected number", - "expression": "[:@:]", - "error": "syntax" - }, - { - "comment": "slice expected number of colon", - "expression": "[:1@]", - "error": "syntax" - } - ] - }, - { - "comment": "Multi-select list syntax", - "given": {"type": "object"}, - "cases": [ - { - "expression": "foo[0]", - "result": null - }, - { - "comment": "Valid multi-select of a list", - "expression": "foo[0, 1]", - "error": "syntax" - }, - { - "expression": "foo.[0]", - "error": "syntax" - }, - { - "expression": "foo.[*]", - "result": null - }, - { - "comment": "Multi-select of a list with trailing comma", - "expression": "foo[0, ]", - "error": "syntax" - }, - { - "comment": "Multi-select of a list with trailing comma and no close", - "expression": "foo[0,", - "error": "syntax" - }, - { - "comment": "Multi-select of a list with trailing comma and no close", - "expression": "foo.[a", - "error": "syntax" - }, - { - "comment": "Multi-select of a list with extra comma", - "expression": "foo[0,, 1]", - "error": "syntax" - }, - { - "comment": "Multi-select of a list using an identifier index", - "expression": "foo[abc]", - "error": "syntax" - }, - { - "comment": "Multi-select of a list using identifier indices", - "expression": "foo[abc, def]", - "error": "syntax" - }, - { - "comment": "Multi-select of a list using an identifier index", - "expression": "foo[abc, 1]", - "error": "syntax" - }, - { - "comment": "Multi-select of a list using an identifier index with trailing comma", - "expression": "foo[abc, ]", - "error": "syntax" - }, - { - "comment": "Valid multi-select of a hash using an identifier index", - "expression": "foo.[abc]", - "result": null - }, - { - "comment": "Valid multi-select of a hash", - "expression": "foo.[abc, def]", - "result": null - }, - { - "comment": "Multi-select of a hash using a numeric index", - "expression": "foo.[abc, 1]", - "error": "syntax" - }, - { - "comment": "Multi-select of a hash with a trailing comma", - "expression": "foo.[abc, ]", - "error": "syntax" - }, - { - "comment": "Multi-select of a hash with extra commas", - "expression": "foo.[abc,, def]", - "error": "syntax" - }, - { - "comment": "Multi-select of a hash using number indices", - "expression": "foo.[0, 1]", - "error": "syntax" - } - ] - }, - { - "comment": "Multi-select hash syntax", - "given": {"type": "object"}, - "cases": [ - { - "comment": "No key or value", - "expression": "a{}", - "error": "syntax" - }, - { - "comment": "No closing token", - "expression": "a{", - "error": "syntax" - }, - { - "comment": "Not a key value pair", - "expression": "a{foo}", - "error": "syntax" - }, - { - "comment": "Missing value and closing character", - "expression": "a{foo:", - "error": "syntax" - }, - { - "comment": "Missing closing character", - "expression": "a{foo: 0", - "error": "syntax" - }, - { - "comment": "Missing value", - "expression": "a{foo:}", - "error": "syntax" - }, - { - "comment": "Trailing comma and no closing character", - "expression": "a{foo: 0, ", - "error": "syntax" - }, - { - "comment": "Missing value with trailing comma", - "expression": "a{foo: ,}", - "error": "syntax" - }, - { - "comment": "Accessing Array using an identifier", - "expression": "a{foo: bar}", - "error": "syntax" - }, - { - "expression": "a{foo: 0}", - "error": "syntax" - }, - { - "comment": "Missing key-value pair", - "expression": "a.{}", - "error": "syntax" - }, - { - "comment": "Not a key-value pair", - "expression": "a.{foo}", - "error": "syntax" - }, - { - "comment": "Valid multi-select hash extraction", - "expression": "a.{foo: bar}", - "result": null - }, - { - "comment": "Valid multi-select hash extraction", - "expression": "a.{foo: bar, baz: bam}", - "result": null - }, - { - "comment": "Trailing comma", - "expression": "a.{foo: bar, }", - "error": "syntax" - }, - { - "comment": "Missing key in second key-value pair", - "expression": "a.{foo: bar, baz}", - "error": "syntax" - }, - { - "comment": "Missing value in second key-value pair", - "expression": "a.{foo: bar, baz:}", - "error": "syntax" - }, - { - "comment": "Trailing comma", - "expression": "a.{foo: bar, baz: bam, }", - "error": "syntax" - }, - { - "comment": "Nested multi select", - "expression": "{\"\\\\\":{\" \":*}}", - "result": {"\\": {" ": ["object"]}} - }, - { - "comment": "Missing closing } after a valid nud", - "expression": "{a: @", - "error": "syntax" - } - ] - }, - { - "comment": "Or expressions", - "given": {"type": "object"}, - "cases": [ - { - "expression": "foo || bar", - "result": null - }, - { - "expression": "foo ||", - "error": "syntax" - }, - { - "expression": "foo.|| bar", - "error": "syntax" - }, - { - "expression": " || foo", - "error": "syntax" - }, - { - "expression": "foo || || foo", - "error": "syntax" - }, - { - "expression": "foo.[a || b]", - "result": null - }, - { - "expression": "foo.[a ||]", - "error": "syntax" - }, - { - "expression": "\"foo", - "error": "syntax" - } - ] - }, - { - "comment": "Filter expressions", - "given": {"type": "object"}, - "cases": [ - { - "expression": "foo[?bar==`\"baz\"`]", - "result": null - }, - { - "expression": "foo[? bar == `\"baz\"` ]", - "result": null - }, - { - "expression": "foo[ ?bar==`\"baz\"`]", - "error": "syntax" - }, - { - "expression": "foo[?bar==]", - "error": "syntax" - }, - { - "expression": "foo[?==]", - "error": "syntax" - }, - { - "expression": "foo[?==bar]", - "error": "syntax" - }, - { - "expression": "foo[?bar==baz?]", - "error": "syntax" - }, - { - "expression": "foo[?a.b.c==d.e.f]", - "result": null - }, - { - "expression": "foo[?bar==`[0, 1, 2]`]", - "result": null - }, - { - "expression": "foo[?bar==`[\"a\", \"b\", \"c\"]`]", - "result": null - }, - { - "comment": "Literal char not escaped", - "expression": "foo[?bar==`[\"foo`bar\"]`]", - "error": "syntax" - }, - { - "comment": "Literal char escaped", - "expression": "foo[?bar==`[\"foo\\`bar\"]`]", - "result": null - }, - { - "comment": "Unknown comparator", - "expression": "foo[?bar<>baz]", - "error": "syntax" - }, - { - "comment": "Unknown comparator", - "expression": "foo[?bar^baz]", - "error": "syntax" - }, - { - "expression": "foo[bar==baz]", - "error": "syntax" - }, - { - "comment": "Quoted identifier in filter expression no spaces", - "expression": "[?\"\\\\\">`\"foo\"`]", - "result": null - }, - { - "comment": "Quoted identifier in filter expression with spaces", - "expression": "[?\"\\\\\" > `\"foo\"`]", - "result": null - } - ] - }, - { - "comment": "Filter expression errors", - "given": {"type": "object"}, - "cases": [ - { - "expression": "bar.`\"anything\"`", - "error": "syntax" - }, - { - "expression": "bar.baz.noexists.`\"literal\"`", - "error": "syntax" - }, - { - "comment": "Literal wildcard projection", - "expression": "foo[*].`\"literal\"`", - "error": "syntax" - }, - { - "expression": "foo[*].name.`\"literal\"`", - "error": "syntax" - }, - { - "expression": "foo[].name.`\"literal\"`", - "error": "syntax" - }, - { - "expression": "foo[].name.`\"literal\"`.`\"subliteral\"`", - "error": "syntax" - }, - { - "comment": "Projecting a literal onto an empty list", - "expression": "foo[*].name.noexist.`\"literal\"`", - "error": "syntax" - }, - { - "expression": "foo[].name.noexist.`\"literal\"`", - "error": "syntax" - }, - { - "expression": "twolen[*].`\"foo\"`", - "error": "syntax" - }, - { - "comment": "Two level projection of a literal", - "expression": "twolen[*].threelen[*].`\"bar\"`", - "error": "syntax" - }, - { - "comment": "Two level flattened projection of a literal", - "expression": "twolen[].threelen[].`\"bar\"`", - "error": "syntax" - }, - { - "comment": "expects closing ]", - "expression": "foo[? @ | @", - "error": "syntax" - } - ] - }, - { - "comment": "Identifiers", - "given": {"type": "object"}, - "cases": [ - { - "expression": "foo", - "result": null - }, - { - "expression": "\"foo\"", - "result": null - }, - { - "expression": "\"\\\\\"", - "result": null - }, - { - "expression": "\"\\u\"", - "error": "syntax" - } - ] - }, - { - "comment": "Combined syntax", - "given": [], - "cases": [ - { - "expression": "*||*|*|*", - "result": null - }, - { - "expression": "*[]||[*]", - "result": [] - }, - { - "expression": "[*.*]", - "result": [null] - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/unicode.json b/lib/jmespath/jmespath/tests/compliance/unicode.json deleted file mode 100644 index 6b07b0b..0000000 --- a/lib/jmespath/jmespath/tests/compliance/unicode.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "given": {"foo": [{"✓": "✓"}, {"✓": "✗"}]}, - "cases": [ - { - "expression": "foo[].\"✓\"", - "result": ["✓", "✗"] - } - ] - }, - { - "given": {"☯": true}, - "cases": [ - { - "expression": "\"☯\"", - "result": true - } - ] - }, - { - "given": {"♪♫•*¨*•.¸¸â¤Â¸Â¸.•*¨*•♫♪": true}, - "cases": [ - { - "expression": "\"♪♫•*¨*•.¸¸â¤Â¸Â¸.•*¨*•♫♪\"", - "result": true - } - ] - }, - { - "given": {"☃": true}, - "cases": [ - { - "expression": "\"☃\"", - "result": true - } - ] - } -] diff --git a/lib/jmespath/jmespath/tests/compliance/wildcard.json b/lib/jmespath/jmespath/tests/compliance/wildcard.json deleted file mode 100644 index 3bcec30..0000000 --- a/lib/jmespath/jmespath/tests/compliance/wildcard.json +++ /dev/null @@ -1,460 +0,0 @@ -[{ - "given": { - "foo": { - "bar": { - "baz": "val" - }, - "other": { - "baz": "val" - }, - "other2": { - "baz": "val" - }, - "other3": { - "notbaz": ["a", "b", "c"] - }, - "other4": { - "notbaz": ["a", "b", "c"] - }, - "other5": { - "other": { - "a": 1, - "b": 1, - "c": 1 - } - } - } - }, - "cases": [ - { - "expression": "foo.*.baz", - "result": ["val", "val", "val"] - }, - { - "expression": "foo.bar.*", - "result": ["val"] - }, - { - "expression": "foo.*.notbaz", - "result": [["a", "b", "c"], ["a", "b", "c"]] - }, - { - "expression": "foo.*.notbaz[0]", - "result": ["a", "a"] - }, - { - "expression": "foo.*.notbaz[-1]", - "result": ["c", "c"] - } - ] -}, { - "given": { - "foo": { - "first-1": { - "second-1": "val" - }, - "first-2": { - "second-1": "val" - }, - "first-3": { - "second-1": "val" - } - } - }, - "cases": [ - { - "expression": "foo.*", - "result": [{"second-1": "val"}, {"second-1": "val"}, - {"second-1": "val"}] - }, - { - "expression": "foo.*.*", - "result": [["val"], ["val"], ["val"]] - }, - { - "expression": "foo.*.*.*", - "result": [[], [], []] - }, - { - "expression": "foo.*.*.*.*", - "result": [[], [], []] - } - ] -}, { - "given": { - "foo": { - "bar": "one" - }, - "other": { - "bar": "one" - }, - "nomatch": { - "notbar": "three" - } - }, - "cases": [ - { - "expression": "*.bar", - "result": ["one", "one"] - } - ] -}, { - "given": { - "top1": { - "sub1": {"foo": "one"} - }, - "top2": { - "sub1": {"foo": "one"} - } - }, - "cases": [ - { - "expression": "*", - "result": [{"sub1": {"foo": "one"}}, - {"sub1": {"foo": "one"}}] - }, - { - "expression": "*.sub1", - "result": [{"foo": "one"}, - {"foo": "one"}] - }, - { - "expression": "*.*", - "result": [[{"foo": "one"}], - [{"foo": "one"}]] - }, - { - "expression": "*.*.foo[]", - "result": ["one", "one"] - }, - { - "expression": "*.sub1.foo", - "result": ["one", "one"] - } - ] -}, -{ - "given": - {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, - "cases": [ - { - "expression": "foo[*].bar", - "result": ["one", "two", "three"] - }, - { - "expression": "foo[*].notbar", - "result": ["four"] - } - ] -}, -{ - "given": - [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}], - "cases": [ - { - "expression": "[*]", - "result": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}] - }, - { - "expression": "[*].bar", - "result": ["one", "two", "three"] - }, - { - "expression": "[*].notbar", - "result": ["four"] - } - ] -}, -{ - "given": { - "foo": { - "bar": [ - {"baz": ["one", "two", "three"]}, - {"baz": ["four", "five", "six"]}, - {"baz": ["seven", "eight", "nine"]} - ] - } - }, - "cases": [ - { - "expression": "foo.bar[*].baz", - "result": [["one", "two", "three"], ["four", "five", "six"], ["seven", "eight", "nine"]] - }, - { - "expression": "foo.bar[*].baz[0]", - "result": ["one", "four", "seven"] - }, - { - "expression": "foo.bar[*].baz[1]", - "result": ["two", "five", "eight"] - }, - { - "expression": "foo.bar[*].baz[2]", - "result": ["three", "six", "nine"] - }, - { - "expression": "foo.bar[*].baz[3]", - "result": [] - } - ] -}, -{ - "given": { - "foo": { - "bar": [["one", "two"], ["three", "four"]] - } - }, - "cases": [ - { - "expression": "foo.bar[*]", - "result": [["one", "two"], ["three", "four"]] - }, - { - "expression": "foo.bar[0]", - "result": ["one", "two"] - }, - { - "expression": "foo.bar[0][0]", - "result": "one" - }, - { - "expression": "foo.bar[0][0][0]", - "result": null - }, - { - "expression": "foo.bar[0][0][0][0]", - "result": null - }, - { - "expression": "foo[0][0]", - "result": null - } - ] -}, -{ - "given": { - "foo": [ - {"bar": [{"kind": "basic"}, {"kind": "intermediate"}]}, - {"bar": [{"kind": "advanced"}, {"kind": "expert"}]}, - {"bar": "string"} - ] - - }, - "cases": [ - { - "expression": "foo[*].bar[*].kind", - "result": [["basic", "intermediate"], ["advanced", "expert"]] - }, - { - "expression": "foo[*].bar[0].kind", - "result": ["basic", "advanced"] - } - ] -}, -{ - "given": { - "foo": [ - {"bar": {"kind": "basic"}}, - {"bar": {"kind": "intermediate"}}, - {"bar": {"kind": "advanced"}}, - {"bar": {"kind": "expert"}}, - {"bar": "string"} - ] - }, - "cases": [ - { - "expression": "foo[*].bar.kind", - "result": ["basic", "intermediate", "advanced", "expert"] - } - ] -}, -{ - "given": { - "foo": [{"bar": ["one", "two"]}, {"bar": ["three", "four"]}, {"bar": ["five"]}] - }, - "cases": [ - { - "expression": "foo[*].bar[0]", - "result": ["one", "three", "five"] - }, - { - "expression": "foo[*].bar[1]", - "result": ["two", "four"] - }, - { - "expression": "foo[*].bar[2]", - "result": [] - } - ] -}, -{ - "given": { - "foo": [{"bar": []}, {"bar": []}, {"bar": []}] - }, - "cases": [ - { - "expression": "foo[*].bar[0]", - "result": [] - } - ] -}, -{ - "given": { - "foo": [["one", "two"], ["three", "four"], ["five"]] - }, - "cases": [ - { - "expression": "foo[*][0]", - "result": ["one", "three", "five"] - }, - { - "expression": "foo[*][1]", - "result": ["two", "four"] - } - ] -}, -{ - "given": { - "foo": [ - [ - ["one", "two"], ["three", "four"] - ], [ - ["five", "six"], ["seven", "eight"] - ], [ - ["nine"], ["ten"] - ] - ] - }, - "cases": [ - { - "expression": "foo[*][0]", - "result": [["one", "two"], ["five", "six"], ["nine"]] - }, - { - "expression": "foo[*][1]", - "result": [["three", "four"], ["seven", "eight"], ["ten"]] - }, - { - "expression": "foo[*][0][0]", - "result": ["one", "five", "nine"] - }, - { - "expression": "foo[*][1][0]", - "result": ["three", "seven", "ten"] - }, - { - "expression": "foo[*][0][1]", - "result": ["two", "six"] - }, - { - "expression": "foo[*][1][1]", - "result": ["four", "eight"] - }, - { - "expression": "foo[*][2]", - "result": [] - }, - { - "expression": "foo[*][2][2]", - "result": [] - }, - { - "expression": "bar[*]", - "result": null - }, - { - "expression": "bar[*].baz[*]", - "result": null - } - ] -}, -{ - "given": { - "string": "string", - "hash": {"foo": "bar", "bar": "baz"}, - "number": 23, - "nullvalue": null - }, - "cases": [ - { - "expression": "string[*]", - "result": null - }, - { - "expression": "hash[*]", - "result": null - }, - { - "expression": "number[*]", - "result": null - }, - { - "expression": "nullvalue[*]", - "result": null - }, - { - "expression": "string[*].foo", - "result": null - }, - { - "expression": "hash[*].foo", - "result": null - }, - { - "expression": "number[*].foo", - "result": null - }, - { - "expression": "nullvalue[*].foo", - "result": null - }, - { - "expression": "nullvalue[*].foo[*].bar", - "result": null - } - ] -}, -{ - "given": { - "string": "string", - "hash": {"foo": "val", "bar": "val"}, - "number": 23, - "array": [1, 2, 3], - "nullvalue": null - }, - "cases": [ - { - "expression": "string.*", - "result": null - }, - { - "expression": "hash.*", - "result": ["val", "val"] - }, - { - "expression": "number.*", - "result": null - }, - { - "expression": "array.*", - "result": null - }, - { - "expression": "nullvalue.*", - "result": null - } - ] -}, -{ - "given": { - "a": [0, 1, 2], - "b": [0, 1, 2] - }, - "cases": [ - { - "expression": "*[0]", - "result": [0, 0] - } - ] -} -] diff --git a/shogdb/include/jansson.h b/shogdb/include/jansson.h new file mode 100644 index 0000000..391c85e --- /dev/null +++ b/shogdb/include/jansson.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_H +#define JANSSON_H + +#include +#include +#include /* for size_t */ + +#include "jansson_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ + +#define JANSSON_MAJOR_VERSION 2 +#define JANSSON_MINOR_VERSION 14 +#define JANSSON_MICRO_VERSION 0 + +/* Micro version is omitted if it's 0 */ +#define JANSSON_VERSION "2.14" + +/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this + for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ +#define JANSSON_VERSION_HEX \ + ((JANSSON_MAJOR_VERSION << 16) | (JANSSON_MINOR_VERSION << 8) | \ + (JANSSON_MICRO_VERSION << 0)) + +/* If __atomic or __sync builtins are available the library is thread + * safe for all read-only functions plus reference counting. */ +#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS +#define JANSSON_THREAD_SAFE_REFCOUNT 1 +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define JANSSON_ATTRS(x) __attribute__(x) +#else +#define JANSSON_ATTRS(x) +#endif + +/* types */ + +typedef enum { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} json_type; + +typedef struct json_t { + json_type type; + volatile size_t refcount; +} json_t; + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else +#define JSON_INTEGER_FORMAT "lld" +#endif +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif + +#define json_typeof(json) ((json)->type) +#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT) +#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY) +#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) +#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) +#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) +#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) +#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) +#define json_boolean_value json_is_true +#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) +#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL) + +/* construction, destruction, reference counting */ + +json_t *json_object(void); +json_t *json_array(void); +json_t *json_string(const char *value); +json_t *json_stringn(const char *value, size_t len); +json_t *json_string_nocheck(const char *value); +json_t *json_stringn_nocheck(const char *value, size_t len); +json_t *json_integer(json_int_t value); +json_t *json_real(double value); +json_t *json_true(void); +json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) +json_t *json_null(void); + +/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */ +#if JSON_HAVE_ATOMIC_BUILTINS +#define JSON_INTERNAL_INCREF(json) \ + __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE) +#define JSON_INTERNAL_DECREF(json) \ + __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE) +#elif JSON_HAVE_SYNC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1) +#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1) +#else +#define JSON_INTERNAL_INCREF(json) (++json->refcount) +#define JSON_INTERNAL_DECREF(json) (--json->refcount) +#endif + +static JSON_INLINE json_t *json_incref(json_t *json) { + if (json && json->refcount != (size_t)-1) + JSON_INTERNAL_INCREF(json); + return json; +} + +/* do not call json_delete directly */ +void json_delete(json_t *json); + +static JSON_INLINE void json_decref(json_t *json) { + if (json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0) + json_delete(json); +} + +#if defined(__GNUC__) || defined(__clang__) +static JSON_INLINE void json_decrefp(json_t **json) { + if (json) { + json_decref(*json); + *json = NULL; + } +} + +#define json_auto_t json_t __attribute__((cleanup(json_decrefp))) +#endif + +/* error reporting */ + +#define JSON_ERROR_TEXT_LENGTH 160 +#define JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct json_error_t { + int line; + int column; + int position; + char source[JSON_ERROR_SOURCE_LENGTH]; + char text[JSON_ERROR_TEXT_LENGTH]; +} json_error_t; + +enum json_error_code { + json_error_unknown, + json_error_out_of_memory, + json_error_stack_overflow, + json_error_cannot_open_file, + json_error_invalid_argument, + json_error_invalid_utf8, + json_error_premature_end_of_input, + json_error_end_of_input_expected, + json_error_invalid_syntax, + json_error_invalid_format, + json_error_wrong_type, + json_error_null_character, + json_error_null_value, + json_error_null_byte_in_key, + json_error_duplicate_key, + json_error_numeric_overflow, + json_error_item_not_found, + json_error_index_out_of_range +}; + +static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) { + return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1]; +} + +/* getters, setters, manipulation */ + +void json_object_seed(size_t seed); +size_t json_object_size(const json_t *object); +json_t *json_object_get(const json_t *object, const char *key) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_object_getn(const json_t *object, const char *key, size_t key_len) + JANSSON_ATTRS((warn_unused_result)); +int json_object_set_new(json_t *object, const char *key, json_t *value); +int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value); +int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); +int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len, + json_t *value); +int json_object_del(json_t *object, const char *key); +int json_object_deln(json_t *object, const char *key, size_t key_len); +int json_object_clear(json_t *object); +int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); +int json_object_update_recursive(json_t *object, json_t *other); +void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); +void *json_object_iter_next(json_t *object, void *iter); +const char *json_object_iter_key(void *iter); +size_t json_object_iter_key_len(void *iter); +json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); + +#define json_object_foreach(object, key, value) \ + for (key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key( \ + json_object_iter_next(object, json_object_key_to_iter(key)))) + +#define json_object_keylen_foreach(object, key, key_len, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key( \ + json_object_iter_next(object, json_object_key_to_iter(key))), \ + key_len = json_object_iter_key_len(json_object_key_to_iter(key))) + +#define json_object_foreach_safe(object, n, key, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) + +#define json_object_keylen_foreach_safe(object, n, key, key_len, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)), \ + key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), key_len = json_object_iter_key_len(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) + +#define json_array_foreach(array, index, value) \ + for (index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) + +static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) { + return json_object_set_new(object, key, json_incref(value)); +} + +static JSON_INLINE int json_object_setn(json_t *object, const char *key, size_t key_len, + json_t *value) { + return json_object_setn_new(object, key, key_len, json_incref(value)); +} + +static JSON_INLINE int json_object_set_nocheck(json_t *object, const char *key, + json_t *value) { + return json_object_set_new_nocheck(object, key, json_incref(value)); +} + +static JSON_INLINE int json_object_setn_nocheck(json_t *object, const char *key, + size_t key_len, json_t *value) { + return json_object_setn_new_nocheck(object, key, key_len, json_incref(value)); +} + +static JSON_INLINE int json_object_iter_set(json_t *object, void *iter, json_t *value) { + return json_object_iter_set_new(object, iter, json_incref(value)); +} + +static JSON_INLINE int json_object_update_new(json_t *object, json_t *other) { + int ret = json_object_update(object, other); + json_decref(other); + return ret; +} + +static JSON_INLINE int json_object_update_existing_new(json_t *object, json_t *other) { + int ret = json_object_update_existing(object, other); + json_decref(other); + return ret; +} + +static JSON_INLINE int json_object_update_missing_new(json_t *object, json_t *other) { + int ret = json_object_update_missing(object, other); + json_decref(other); + return ret; +} + +size_t json_array_size(const json_t *array); +json_t *json_array_get(const json_t *array, size_t index) + JANSSON_ATTRS((warn_unused_result)); +int json_array_set_new(json_t *array, size_t index, json_t *value); +int json_array_append_new(json_t *array, json_t *value); +int json_array_insert_new(json_t *array, size_t index, json_t *value); +int json_array_remove(json_t *array, size_t index); +int json_array_clear(json_t *array); +int json_array_extend(json_t *array, json_t *other); + +static JSON_INLINE int json_array_set(json_t *array, size_t ind, json_t *value) { + return json_array_set_new(array, ind, json_incref(value)); +} + +static JSON_INLINE int json_array_append(json_t *array, json_t *value) { + return json_array_append_new(array, json_incref(value)); +} + +static JSON_INLINE int json_array_insert(json_t *array, size_t ind, json_t *value) { + return json_array_insert_new(array, ind, json_incref(value)); +} + +const char *json_string_value(const json_t *string); +size_t json_string_length(const json_t *string); +json_int_t json_integer_value(const json_t *integer); +double json_real_value(const json_t *real); +double json_number_value(const json_t *json); + +int json_string_set(json_t *string, const char *value); +int json_string_setn(json_t *string, const char *value, size_t len); +int json_string_set_nocheck(json_t *string, const char *value); +int json_string_setn_nocheck(json_t *string, const char *value, size_t len); +int json_integer_set(json_t *integer, json_int_t value); +int json_real_set(json_t *real, double value); + +/* pack, unpack */ + +json_t *json_pack(const char *fmt, ...) JANSSON_ATTRS((warn_unused_result)); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) + JANSSON_ATTRS((warn_unused_result)); + +#define JSON_VALIDATE_ONLY 0x1 +#define JSON_STRICT 0x2 + +int json_unpack(json_t *root, const char *fmt, ...); +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, + va_list ap); + +/* sprintf */ + +json_t *json_sprintf(const char *fmt, ...) + JANSSON_ATTRS((warn_unused_result, format(printf, 1, 2))); +json_t *json_vsprintf(const char *fmt, va_list ap) + JANSSON_ATTRS((warn_unused_result, format(printf, 1, 0))); + +/* equality */ + +int json_equal(const json_t *value1, const json_t *value2); + +/* copying */ + +json_t *json_copy(json_t *value) JANSSON_ATTRS((warn_unused_result)); +json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS((warn_unused_result)); + +/* decoding */ + +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 +#define JSON_ALLOW_NUL 0x10 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + +json_t *json_loads(const char *input, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadfd(int input, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) + JANSSON_ATTRS((warn_unused_result)); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, + json_error_t *error) JANSSON_ATTRS((warn_unused_result)); + +/* encoding */ + +#define JSON_MAX_INDENT 0x1F +#define JSON_INDENT(n) ((n)&JSON_MAX_INDENT) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 +#define JSON_REAL_PRECISION(n) (((n)&0x1F) << 11) +#define JSON_EMBED 0x10000 + +typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); + +char *json_dumps(const json_t *json, size_t flags) JANSSON_ATTRS((warn_unused_result)); +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); +int json_dumpf(const json_t *json, FILE *output, size_t flags); +int json_dumpfd(const json_t *json, int output, size_t flags); +int json_dump_file(const json_t *json, const char *path, size_t flags); +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, + size_t flags); + +/* custom memory allocation */ + +typedef void *(*json_malloc_t)(size_t); +typedef void (*json_free_t)(void *); + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn); + +/* runtime version checking */ + +const char *jansson_version_str(void); +int jansson_version_cmp(int major, int minor, int micro); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/shogdb/include/jansson_config.h b/shogdb/include/jansson_config.h new file mode 100644 index 0000000..9afec5f --- /dev/null +++ b/shogdb/include/jansson_config.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The CMake system will generate the jansson_config.h file and + * copy it to the build and install directories. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* Define this so that we can disable scattered automake configuration in source files */ +#ifndef JANSSON_USING_CMAKE +#define JANSSON_USING_CMAKE +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG 1 + +/* Bring in the cmake-detected defines */ +#define HAVE_STDINT_H 1 +/* #undef HAVE_INTTYPES_H */ +#define HAVE_SYS_TYPES_H 1 + +/* Include our standard type header for the integer typedef */ + +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_SYS_TYPES_H) +# include +#endif + + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE inline +#endif + + +#define json_int_t long long +#define json_strtoint strtoll +#define JSON_INTEGER_FORMAT "lld" + + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS 1 + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS 1 + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/shogdb/include/janssonpath.h b/shogdb/include/janssonpath.h new file mode 100644 index 0000000..25c1535 --- /dev/null +++ b/shogdb/include/janssonpath.h @@ -0,0 +1,18 @@ +#ifndef JANSSONPATH +#define JANSSONPATH + +#include + +// for simple jsonpath like $.a.b[0] we got a non-collection +// but for $[*] $[1:12], etc. we got a collection +// note distinct shuld be made dealing with intermidiate result +// so use path_result instead of json_t* +typedef struct path_result { + json_t *result; + int is_collection; +} path_result; + +json_t *json_path_get(json_t *json, const char *path); +path_result json_path_get_distinct(json_t *json, const char *path); + +#endif \ No newline at end of file diff --git a/shogdb/makefile b/shogdb/makefile index 32652d3..5120fed 100644 --- a/shogdb/makefile +++ b/shogdb/makefile @@ -7,7 +7,7 @@ CC = gcc endif CFLAGS += -g -std=c11 -D_GNU_SOURCE -I ../netlibc/include $$(pkg-config --cflags openssl) -LDFLAGS += -pthread $$(pkg-config --libs openssl) +LDFLAGS += -pthread $$(pkg-config --libs openssl) -ljansson # sanitizers (only on linux) ifeq ($(shell uname), Linux) @@ -15,7 +15,9 @@ CFLAGS_SANITIZED += -fsanitize=address,undefined,leak LDFLAGS_SANITIZED += -lasan -lubsan endif -STATIC_LIBS = ../target/cjson.a ../target/tomlc.a ../target/sonic.a ../target/libnetlibc.a ./target/libjmespathimpl.a +STATIC_LIBS = ../target/cjson.a ../target/tomlc.a ../target/sonic.a ../target/libnetlibc.a ./target/janssonpath.o + +.PHONY: ./target/janssonpath.o all: build @@ -62,9 +64,9 @@ run-example: target-dir $(CC) $(CFLAGS_SANITIZED) $(CFLAGS) $(LDFLAGS_SANITIZED) ./src/main.c ./src/client/client.c ./src/db/db.c ./src/db/dht.c ./src/db/pins.c ./src/db/json.c ./src/hashmap/hashmap.c $(STATIC_LIBS) $(LDFLAGS) -o ./target/shogdb -./target/libjmespathimpl.a: - cd ../lib/jmespath/impl/ && cargo build --release - cp ../lib/jmespath/impl/target/release/libjmespathimpl.a ./target/libjmespathimpl.a +./target/janssonpath.o: + cd ../lib/janssonpath/ && make + cp ../lib/janssonpath/janssonpath.o ./target/janssonpath.o target-dir: mkdir -p target diff --git a/shogdb/src/cli/cli.c b/shogdb/src/cli/cli.c index bde1e18..235cd0f 100644 --- a/shogdb/src/cli/cli.c +++ b/shogdb/src/cli/cli.c @@ -1,6 +1,6 @@ #include +#include -#include "../../include/cjson.h" #include "../client/client.h" int main(int argc, char **argv) { @@ -31,37 +31,57 @@ int main(int argc, char **argv) { continue; } + char *token = strtok(input, " "); + // Check if the user wants to exit - if (strcmp(input, "exit") == 0) { + if (strcmp(token, "exit") == 0) { break; - } else if (strcmp(input, "help") == 0) { + } else if (strcmp(token, "help") == 0) { printf("Commands:\n"); printf("help - show this screen\n"); printf("exit - exit the program\n"); printf("print - print all the keys and their values\n"); printf("address - show the server address\n"); printf("\n"); - } else if (strcmp(input, "print") == 0) { + } else if (strcmp(token, "print") == 0) { char *res_all = UNWRAP(shogdb_print(db_ctx)); printf("%s\n", res_all); - } else if (strcmp(input, "address") == 0) { + } else if (strcmp(token, "address") == 0) { printf("%s\n", address); - } else if (strncmp(input, "GET ", 4) == 0) { - char *key = &input[4]; + } else if (strcmp(token, "GET") == 0) { + char *key = &token[4]; db_value_t *res = UNWRAP(shogdb_get(db_ctx, key)); char *res_str = shogdb_print_value(res); printf("%s\n", res_str); - } else if (strncmp(input, "SET ", 4) == 0) { - // char *key = &input[4]; + } else if (strcmp(token, "JSON.GET") == 0) { + char *key = strtok(NULL, " "); + char *filter = strtok(NULL, " "); + + db_value_t *res = UNWRAP(shogdb_json_get(db_ctx, key, filter)); + char *res_str = shogdb_print_value(res); + + printf("%s\n", res_str); + } else if (strcmp(token, "JSON.SET") == 0) { + char *key = strtok(NULL, " "); + char *value = strtok(NULL, " "); + + UNWRAP(shogdb_set_json(db_ctx, key, value)); + + printf("OK\n"); + } else if (strcmp(token, "STR.SET") == 0) { + char *key = strtok(NULL, " "); + + char *value = nstrdup(strtok(NULL, " ")); + value = &value[1]; + value[strlen(value - 2)] = '\0'; - // db_value_t *res = UNWRAP(shogdb_get(db_ctx, key)); - // char *res_str = shogdb_print_value(res); + UNWRAP(shogdb_set_str(db_ctx, key, value)); printf("OK\n"); } else { - printf("invalid command `%s`\n", input); + printf("invalid command `%s`\n", token); } } while (1); diff --git a/shogdb/src/client/client.c b/shogdb/src/client/client.c index 440bd3f..4d360de 100644 --- a/shogdb/src/client/client.c +++ b/shogdb/src/client/client.c @@ -9,7 +9,7 @@ ****/ #include "client.h" -#include "../../include/cjson.h" +#include "../../include/jansson.h" #include "../../include/sonic.h" #include @@ -18,6 +18,21 @@ #include #include +void *str_to_json(char *str) { + json_error_t error; + void *value = json_loads(str, 0, &error); + + return value; +}; + +void free_json(void *json) { json_decref(json); } + +char *json_to_str(void *json) { + char *str = json_dumps(json, JSON_INDENT(0)); + + return str; +} + result_t shogdb_set(shogdb_ctx_t *ctx, char *key, char *value) { char url[256]; sprintf(url, "%s/set/%s", ctx->address, key); @@ -301,7 +316,7 @@ void free_db_value(db_value_t *value) { } if (value->value_type == VALUE_JSON) { - cJSON_Delete(value->value_json); + free_json(value->value_json); } pthread_mutex_destroy(&value->mutex); @@ -369,7 +384,11 @@ result_t shogdb_parse_message(char *msg) { value->value_float = result; } else if (value->value_type == VALUE_JSON) { - value->value_json = cJSON_Parse(msg_value); + // LOG(INFO, "MSG: %s", msg_value); + + value->value_json = str_to_json(msg_value); + + // LOG(INFO, "HERE END!!!"); if (value->value_json == NULL) { return ERR("could not parse JSON"); @@ -384,3 +403,44 @@ result_t shogdb_parse_message(char *msg) { return OK(value); } + +char *shogdb_print_value(db_value_t *value) { + char *value_type = value_type_to_str(value->value_type); + + char *res = NULL; + + if (value->value_type == VALUE_STR) { + res = string_from(value_type, " ", value->value_str, NULL); + } else if (value->value_type == VALUE_ERR) { + res = string_from(value_type, " ", value->value_err, NULL); + } else if (value->value_type == VALUE_BOOL) { + if (value->value_bool == 1) { + res = string_from(value_type, " true", NULL); + } else { + res = string_from(value_type, " false", NULL); + } + } else if (value->value_type == VALUE_UINT) { + char val[256]; + sprintf(val, U64_FORMAT_SPECIFIER, value->value_uint); + + res = string_from(value_type, " ", val, NULL); + } else if (value->value_type == VALUE_INT) { + char val[256]; + sprintf(val, S64_FORMAT_SPECIFIER, value->value_int); + + res = string_from(value_type, " ", val, NULL); + } else if (value->value_type == VALUE_FLOAT) { + char val[256]; + sprintf(val, F64_FORMAT_SPECIFIER, value->value_float); + + res = string_from(value_type, " ", val, NULL); + } else if (value->value_type == VALUE_JSON) { + char *str = json_to_str(value->value_json); + res = string_from(value_type, " ", str, NULL); + free(str); + } else { + PANIC("unhandled type"); + } + + return res; +} diff --git a/shogdb/src/client/client.h b/shogdb/src/client/client.h index 609bb53..92142e4 100644 --- a/shogdb/src/client/client.h +++ b/shogdb/src/client/client.h @@ -79,6 +79,8 @@ char *value_type_to_str(value_type_t value_type); void free_db_value(db_value_t *value); +char *shogdb_print_value(db_value_t *value); + result_t shogdb_parse_message(char *msg); #endif diff --git a/shogdb/src/db/db.c b/shogdb/src/db/db.c index f43f8cb..3498cba 100644 --- a/shogdb/src/db/db.c +++ b/shogdb/src/db/db.c @@ -9,6 +9,7 @@ ****/ #include "db.h" +#include "../../include/cjson.h" #include "../../include/sonic.h" #include "../hashmap/hashmap.h" #include "dht.h" @@ -22,6 +23,8 @@ #include #include +#include + db_ctx_t *global_ctx = NULL; // FIXME: serialize seems to not include the latest data from a DHT item's @@ -64,7 +67,7 @@ char *serialize_data(db_ctx_t *ctx) { cJSON_AddStringToObject(item_json, "value", val); } else if (value->value_type == VALUE_JSON) { assert(value->value_json != NULL); - char *val = cJSON_Print(value->value_json); + char *val = json_to_str(value->value_json); cJSON_AddStringToObject(item_json, "value", val); @@ -179,9 +182,9 @@ void db_add_float_value(db_ctx_t *ctx, char *key, f64 val) { ctx->saved = false; } -void db_add_json_value(db_ctx_t *ctx, char *key, cJSON *val) { - char *str = cJSON_Print(val); - cJSON *new_val = cJSON_Parse(str); +void db_add_json_value(db_ctx_t *ctx, char *key, void *val) { + char *str = json_to_str(val); + void *new_val = str_to_json(str); free(str); db_value_t *value = new_db_value(VALUE_JSON); @@ -288,47 +291,6 @@ result_t db_update_float_value(db_ctx_t *ctx, char *key, f64 val) { return OK(NULL); } -char *print_value(db_value_t *value) { - char *value_type = value_type_to_str(value->value_type); - - char *res = NULL; - - if (value->value_type == VALUE_STR) { - res = string_from(value_type, " ", value->value_str, NULL); - } else if (value->value_type == VALUE_ERR) { - res = string_from(value_type, " ", value->value_err, NULL); - } else if (value->value_type == VALUE_BOOL) { - if (value->value_bool == 1) { - res = string_from(value_type, " true", NULL); - } else { - res = string_from(value_type, " false", NULL); - } - } else if (value->value_type == VALUE_UINT) { - char val[256]; - sprintf(val, U64_FORMAT_SPECIFIER, value->value_uint); - - res = string_from(value_type, " ", val, NULL); - } else if (value->value_type == VALUE_INT) { - char val[256]; - sprintf(val, S64_FORMAT_SPECIFIER, value->value_int); - - res = string_from(value_type, " ", val, NULL); - } else if (value->value_type == VALUE_FLOAT) { - char val[256]; - sprintf(val, F64_FORMAT_SPECIFIER, value->value_float); - - res = string_from(value_type, " ", val, NULL); - } else if (value->value_type == VALUE_JSON) { - char *str = cJSON_Print(value->value_json); - res = string_from(value_type, " ", str, NULL); - free(str); - } else { - PANIC("unhandled type"); - } - - return res; -} - void home_route(sonic_server_request_t *req) { sonic_server_response_t *resp = sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); @@ -349,7 +311,7 @@ void get_route(sonic_server_request_t *req) { if (is_ok(res)) { db_value_t *value = VALUE(res); - char *body = print_value(value); + char *body = shogdb_print_value(value); sonic_server_response_t *resp = sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); @@ -650,11 +612,11 @@ result_t db_restore_data(db_ctx_t *ctx) { PANIC("invalid boolean value"); } } else if (value_type == VALUE_JSON) { - cJSON *result = cJSON_Parse(value_str); + void *result = str_to_json(value_str); db_add_json_value(ctx, key, result); - cJSON_Delete(result); + free_json(result); } else { PANIC("unhandled value type"); } diff --git a/shogdb/src/db/db.h b/shogdb/src/db/db.h index 84fa3b7..9f55ce7 100644 --- a/shogdb/src/db/db.h +++ b/shogdb/src/db/db.h @@ -11,7 +11,6 @@ #ifndef SHOGDB_H #define SHOGDB_H -#include "../../include/cjson.h" #include "../../include/sonic.h" #include "../hashmap/hashmap.h" #include "../client/client.h" @@ -47,7 +46,7 @@ typedef struct { result_t db_get_value(db_ctx_t *ctx, char *key); -void db_add_json_value(db_ctx_t *ctx, char *key, cJSON *val); +void db_add_json_value(db_ctx_t *ctx, char *key, void *val); result_t start_db(db_ctx_t *ctx); diff --git a/shogdb/src/db/dht.c b/shogdb/src/db/dht.c index 711f575..3c7d688 100644 --- a/shogdb/src/db/dht.c +++ b/shogdb/src/db/dht.c @@ -14,571 +14,571 @@ db_ctx_t *dht_ctx = NULL; -void dht_peer_clear_pins_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_peer_clear_pins_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - char *item_node_id = - cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; +// cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// char *item_node_id = +// cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; - if (strcmp(req_body, item_node_id) == 0) { - cJSON_ReplaceItemInObjectCaseSensitive(item_json, "pins", - cJSON_CreateArray()); +// if (strcmp(req_body, item_node_id) == 0) { +// cJSON_ReplaceItemInObjectCaseSensitive(item_json, "pins", +// cJSON_CreateArray()); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } - } +// return; +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char *body = "ERR peer not found"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "ERR peer not found"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// return; +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_get_peers_with_pin_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_get_peers_with_pin_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - cJSON *peers_with_pin = cJSON_CreateArray(); +// cJSON *peers_with_pin = cJSON_CreateArray(); - cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - cJSON *pins = cJSON_GetObjectItemCaseSensitive(item_json, "pins"); +// cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// cJSON *pins = cJSON_GetObjectItemCaseSensitive(item_json, "pins"); - cJSON *pin_json = NULL; - cJSON_ArrayForEach(pin_json, pins) { - char *pin_shoggoth_id = pin_json->valuestring; +// cJSON *pin_json = NULL; +// cJSON_ArrayForEach(pin_json, pins) { +// char *pin_shoggoth_id = pin_json->valuestring; - if (strcmp(pin_shoggoth_id, req_body) == 0) { - cJSON_AddItemReferenceToArray(peers_with_pin, item_json); - break; - } - } - } +// if (strcmp(pin_shoggoth_id, req_body) == 0) { +// cJSON_AddItemReferenceToArray(peers_with_pin, item_json); +// break; +// } +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char *body = cJSON_Print(peers_with_pin); - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = cJSON_Print(peers_with_pin); +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(body); - cJSON_Delete(peers_with_pin); - sonic_free_server_response(resp); +// free(body); +// cJSON_Delete(peers_with_pin); +// sonic_free_server_response(resp); - return; - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// return; +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_peer_pins_add_resource_route(sonic_server_request_t *req) { - char *node_id = sonic_get_path_segment(req, "node_id"); +// void dht_peer_pins_add_resource_route(sonic_server_request_t *req) { +// char *node_id = sonic_get_path_segment(req, "node_id"); - result_t res = db_get_value(dht_ctx, "dht"); +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - char *shoggoth_id = req_body; +// char *shoggoth_id = req_body; - cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - char *item_node_id = - cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; +// cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// char *item_node_id = +// cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; - if (strcmp(node_id, item_node_id) == 0) { - cJSON *pins = cJSON_GetObjectItemCaseSensitive(item_json, "pins"); +// if (strcmp(node_id, item_node_id) == 0) { +// cJSON *pins = cJSON_GetObjectItemCaseSensitive(item_json, "pins"); - cJSON *pin_json = cJSON_CreateString(shoggoth_id); - cJSON_AddItemToArray(pins, pin_json); +// cJSON *pin_json = cJSON_CreateString(shoggoth_id); +// cJSON_AddItemToArray(pins, pin_json); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } - } +// return; +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char *body = "ERR peer not found"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "ERR peer not found"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// return; +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void get_dht_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void get_dht_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); - if (value->value_type == VALUE_JSON) { - char *str = cJSON_Print(value->value_json); +// if (value->value_type == VALUE_JSON) { +// char *str = cJSON_Print(value->value_json); - char *body = malloc((strlen(str) + 10) * sizeof(char)); - sprintf(body, "%s %s", value_type_to_str(VALUE_JSON), str); +// char *body = malloc((strlen(str) + 10) * sizeof(char)); +// sprintf(body, "%s %s", value_type_to_str(VALUE_JSON), str); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(str); - free(body); +// free(str); +// free(body); - sonic_free_server_response(resp); - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_free_server_response(resp); +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR dht is not a JSON type"); +// char err[256]; +// sprintf(err, "ERR dht is not a JSON type"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_remove_item_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_remove_item_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - char *node_id = req_body; - int index = 0; +// char *node_id = req_body; +// int index = 0; - const cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - char *item_node_id = - cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; +// const cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// char *item_node_id = +// cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; - if (strcmp(node_id, item_node_id) == 0) { - cJSON_DeleteItemFromArray(dht, index); +// if (strcmp(node_id, item_node_id) == 0) { +// cJSON_DeleteItemFromArray(dht, index); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(req_body); - sonic_free_server_response(resp); +// free(req_body); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } +// return; +// } - index++; - } +// index++; +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR item with node_id not found"); +// char err[256]; +// sprintf(err, "ERR item with node_id not found"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_get_unreachable_count_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_get_unreachable_count_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - free_result(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// free_result(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - char *node_id = req_body; +// char *node_id = req_body; - const cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - char *item_node_id = - cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; +// const cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// char *item_node_id = +// cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; - if (strcmp(node_id, item_node_id) == 0) { - int unreachable_count = - cJSON_GetObjectItemCaseSensitive(item_json, "unreachable_count") - ->valueint; +// if (strcmp(node_id, item_node_id) == 0) { +// int unreachable_count = +// cJSON_GetObjectItemCaseSensitive(item_json, "unreachable_count") +// ->valueint; - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char body[256]; - sprintf(body, "\"%d\"", unreachable_count); +// char body[256]; +// sprintf(body, "\"%d\"", unreachable_count); - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(req_body); - sonic_free_server_response(resp); +// free(req_body); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } - } +// return; +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR item with node_id not found"); +// char err[256]; +// sprintf(err, "ERR item with node_id not found"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } else { - free_result(res); - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } else { +// free_result(res); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_increment_unreachable_count_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_increment_unreachable_count_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - free_result(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// free_result(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - char *node_id = req_body; +// char *node_id = req_body; - const cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - char *item_node_id = - cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; +// const cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// char *item_node_id = +// cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; - if (strcmp(node_id, item_node_id) == 0) { - cJSON *unreachable_count = - cJSON_GetObjectItemCaseSensitive(item_json, "unreachable_count"); +// if (strcmp(node_id, item_node_id) == 0) { +// cJSON *unreachable_count = +// cJSON_GetObjectItemCaseSensitive(item_json, "unreachable_count"); - unreachable_count->valueint++; +// unreachable_count->valueint++; - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; +// char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(req_body); - sonic_free_server_response(resp); +// free(req_body); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } - } +// return; +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR item with node_id not found"); +// char err[256]; +// sprintf(err, "ERR item with node_id not found"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } else { - free_result(res); - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } else { +// free_result(res); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_reset_unreachable_count_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_reset_unreachable_count_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *dht = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *dht = value->value_json; - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - char *node_id = req_body; +// char *node_id = req_body; - const cJSON *item_json = NULL; - cJSON_ArrayForEach(item_json, dht) { - char *item_node_id = - cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; +// const cJSON *item_json = NULL; +// cJSON_ArrayForEach(item_json, dht) { +// char *item_node_id = +// cJSON_GetObjectItemCaseSensitive(item_json, "node_id")->valuestring; - if (strcmp(node_id, item_node_id) == 0) { - cJSON *unreachable_count = - cJSON_GetObjectItemCaseSensitive(item_json, "unreachable_count"); +// if (strcmp(node_id, item_node_id) == 0) { +// cJSON *unreachable_count = +// cJSON_GetObjectItemCaseSensitive(item_json, "unreachable_count"); - unreachable_count->valueint = 0; +// unreachable_count->valueint = 0; - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; +// char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(req_body); - sonic_free_server_response(resp); +// free(req_body); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } - } +// return; +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR item with node_id not found"); +// char err[256]; +// sprintf(err, "ERR item with node_id not found"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void dht_add_item_route(sonic_server_request_t *req) { - result_t res = db_get_value(dht_ctx, "dht"); +// void dht_add_item_route(sonic_server_request_t *req) { +// result_t res = db_get_value(dht_ctx, "dht"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - free_result(res); +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// free_result(res); - char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); - strncpy(req_body, req->request_body, req->request_body_size); - req_body[req->request_body_size] = '\0'; +// char *req_body = malloc((req->request_body_size + 1) * sizeof(char)); +// strncpy(req_body, req->request_body, req->request_body_size); +// req_body[req->request_body_size] = '\0'; - cJSON *item_json = cJSON_Parse(req_body); - if (item_json == NULL) { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// cJSON *item_json = cJSON_Parse(req_body); +// if (item_json == NULL) { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR could not parse JSON"); +// char err[256]; +// sprintf(err, "ERR could not parse JSON"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); - return; - } +// return; +// } - free(req_body); +// free(req_body); - cJSON_AddItemToArray(value->value_json, item_json); +// cJSON_AddItemToArray(value->value_json, item_json); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - dht_ctx->saved = false; +// dht_ctx->saved = false; - return; - } else { - free_result(res); +// return; +// } else { +// free_result(res); - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); - return; - } -} +// return; +// } +// } void add_dht_routes(sonic_server_t *server) { - sonic_add_route(server, "/dht/get_dht", METHOD_GET, get_dht_route); - sonic_add_route(server, "/dht/add_item", METHOD_GET, dht_add_item_route); - sonic_add_route(server, "/dht/remove_item", METHOD_GET, - dht_remove_item_route); - sonic_add_route(server, "/dht/peer_clear_pins", METHOD_GET, - dht_peer_clear_pins_route); - sonic_add_route(server, "/dht/peer_pins_add_resource/{node_id}", METHOD_GET, - dht_peer_pins_add_resource_route); - sonic_add_route(server, "/dht/get_peers_with_pin", METHOD_GET, - dht_get_peers_with_pin_route); - sonic_add_route(server, "/dht/get_unreachable_count", METHOD_GET, - dht_get_unreachable_count_route); - sonic_add_route(server, "/dht/reset_unreachable_count", METHOD_GET, - dht_reset_unreachable_count_route); - sonic_add_route(server, "/dht/increment_unreachable_count", METHOD_GET, - dht_increment_unreachable_count_route); + // sonic_add_route(server, "/dht/get_dht", METHOD_GET, get_dht_route); + // sonic_add_route(server, "/dht/add_item", METHOD_GET, dht_add_item_route); + // sonic_add_route(server, "/dht/remove_item", METHOD_GET, + // dht_remove_item_route); + // sonic_add_route(server, "/dht/peer_clear_pins", METHOD_GET, + // dht_peer_clear_pins_route); + // sonic_add_route(server, "/dht/peer_pins_add_resource/{node_id}", METHOD_GET, + // dht_peer_pins_add_resource_route); + // sonic_add_route(server, "/dht/get_peers_with_pin", METHOD_GET, + // dht_get_peers_with_pin_route); + // sonic_add_route(server, "/dht/get_unreachable_count", METHOD_GET, + // dht_get_unreachable_count_route); + // sonic_add_route(server, "/dht/reset_unreachable_count", METHOD_GET, + // dht_reset_unreachable_count_route); + // sonic_add_route(server, "/dht/increment_unreachable_count", METHOD_GET, + // dht_increment_unreachable_count_route); } result_t setup_dht(db_ctx_t *ctx) { dht_ctx = ctx; - cJSON *value_json = cJSON_CreateArray(); + // cJSON *value_json = cJSON_CreateArray(); - db_add_json_value(ctx, "dht", value_json); + // db_add_json_value(ctx, "dht", value_json); - cJSON_Delete(value_json); + // cJSON_Delete(value_json); return OK(NULL); } diff --git a/shogdb/src/db/jmespath.cpp b/shogdb/src/db/jmespath.cpp deleted file mode 100644 index 710a32d..0000000 --- a/shogdb/src/db/jmespath.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -namespace jp = jmespath; - -char *jmespath_filter_json(char *json_str, char *filter) { - jp::Expression expression = filter; - - char *res = jp::search(expression, json_str); - - return res; -} diff --git a/shogdb/src/db/json.c b/shogdb/src/db/json.c index 7d4bd2b..299c48d 100644 --- a/shogdb/src/db/json.c +++ b/shogdb/src/db/json.c @@ -8,31 +8,30 @@ * ****/ +#include "../../include/jansson.h" +#include "../../include/janssonpath.h" #include "../../include/sonic.h" #include "db.h" +#include "json.h" #include "pins.h" #include db_ctx_t *json_ctx = NULL; -extern char *jmespath_filter_json(char *json, char *filter); - -cJSON *filter_json(cJSON *json, char *filter) { - char *json_str = cJSON_Print(json); - - char *value_str = jmespath_filter_json(json_str, filter); - free(json_str); - - cJSON *value_json = cJSON_Parse(value_str); - free(value_str); +void *filter_json(void *json, char *filter) { + void *value_json = json_path_get(json, filter); return value_json; } +void add_item_to_array(void *json, void *item) { + json_array_append(json, item); +} + void json_append_route(sonic_server_request_t *req) { char *key = sonic_get_path_segment(req, "key"); - // char *filter = sonic_get_path_segment(req, "filter"); + char *filter = sonic_get_path_segment(req, "filter"); char *new_value = nmalloc((req->request_body_size + 1) * sizeof(char)); memcpy(new_value, req->request_body, req->request_body_size); @@ -43,12 +42,12 @@ void json_append_route(sonic_server_request_t *req) { if (is_ok(res)) { db_value_t *value = VALUE(res); - // cJSON *filtered_value = filter_json(value->value_json, filter); + void *filtered_value = filter_json(value->value_json, filter); - cJSON *new_value_json = cJSON_Parse(new_value); + void *new_value_json = str_to_json(new_value); nfree(new_value); - cJSON_AddItemToArray(value->value_json, new_value_json); + add_item_to_array(filtered_value, new_value_json); sonic_server_response_t *resp = sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); @@ -86,9 +85,9 @@ void json_get_route(sonic_server_request_t *req) { if (is_ok(res)) { db_value_t *value = VALUE(res); - cJSON *filtered_value = filter_json(value->value_json, filter); + void *filtered_value = filter_json(value->value_json, filter); - char *str = cJSON_Print(filtered_value); + char *str = json_to_str(filtered_value); char *body = string_from("JSON", " ", str, NULL); free(str); diff --git a/shogdb/src/db/json.h b/shogdb/src/db/json.h index 195013b..7706bfb 100644 --- a/shogdb/src/db/json.h +++ b/shogdb/src/db/json.h @@ -19,4 +19,8 @@ void add_json_routes(db_ctx_t *ctx, sonic_server_t *server); +void *str_to_json(char *str); +char *json_to_str(void *json); +void free_json(void *json); + #endif diff --git a/shogdb/src/db/pins.c b/shogdb/src/db/pins.c index 91f4f41..6c1e5d1 100644 --- a/shogdb/src/db/pins.c +++ b/shogdb/src/db/pins.c @@ -14,264 +14,263 @@ db_ctx_t *pins_ctx = NULL; -void pins_remove_resource_route(sonic_server_request_t *req) { - char *shoggoth_id = sonic_get_path_segment(req, "shoggoth_id"); +// void pins_remove_resource_route(sonic_server_request_t *req) { +// char *shoggoth_id = sonic_get_path_segment(req, "shoggoth_id"); - result_t res = db_get_value(pins_ctx, "pins"); +// result_t res = db_get_value(pins_ctx, "pins"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *pins = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *pins = value->value_json; - int index = 0; +// int index = 0; - const cJSON *pin_json = NULL; - cJSON_ArrayForEach(pin_json, pins) { - // char *pin_shoggoth_id = pin_json->valuestring; +// const cJSON *pin_json = NULL; +// cJSON_ArrayForEach(pin_json, pins) { - char *pin_shoggoth_id = - cJSON_GetObjectItemCaseSensitive(pin_json, "shoggoth_id") - ->valuestring; +// char *pin_shoggoth_id = +// cJSON_GetObjectItemCaseSensitive(pin_json, "shoggoth_id") +// ->valuestring; - if (strcmp(shoggoth_id, pin_shoggoth_id) == 0) { - cJSON_DeleteItemFromArray(pins, index); +// if (strcmp(shoggoth_id, pin_shoggoth_id) == 0) { +// cJSON_DeleteItemFromArray(pins, index); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - pins_ctx->saved = false; +// pins_ctx->saved = false; - return; - } +// return; +// } - index++; - } +// index++; +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR pin with shoggoth_id not found"); +// char err[256]; +// sprintf(err, "ERR pin with shoggoth_id not found"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void get_pin_label_route(sonic_server_request_t *req) { - char *shoggoth_id = sonic_get_path_segment(req, "shoggoth_id"); +// void get_pin_label_route(sonic_server_request_t *req) { +// char *shoggoth_id = sonic_get_path_segment(req, "shoggoth_id"); - result_t res = db_get_value(pins_ctx, "pins"); +// result_t res = db_get_value(pins_ctx, "pins"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); - cJSON *pins = value->value_json; +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); +// cJSON *pins = value->value_json; - const cJSON *pin_json = NULL; - cJSON_ArrayForEach(pin_json, pins) { +// const cJSON *pin_json = NULL; +// cJSON_ArrayForEach(pin_json, pins) { - char *pin_shoggoth_id = - cJSON_GetObjectItemCaseSensitive(pin_json, "shoggoth_id") - ->valuestring; +// char *pin_shoggoth_id = +// cJSON_GetObjectItemCaseSensitive(pin_json, "shoggoth_id") +// ->valuestring; - if (strcmp(shoggoth_id, pin_shoggoth_id) == 0) { - char *label = - cJSON_GetObjectItemCaseSensitive(pin_json, "label")->valuestring; +// if (strcmp(shoggoth_id, pin_shoggoth_id) == 0) { +// char *label = +// cJSON_GetObjectItemCaseSensitive(pin_json, "label")->valuestring; - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = label; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = label; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - return; - } - } +// return; +// } +// } - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR pin with shoggoth_id not found"); +// char err[256]; +// sprintf(err, "ERR pin with shoggoth_id not found"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void pins_clear_route(sonic_server_request_t *req) { - result_t res = db_get_value(pins_ctx, "pins"); +// void pins_clear_route(sonic_server_request_t *req) { +// result_t res = db_get_value(pins_ctx, "pins"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); - cJSON_Delete(value->value_json); +// cJSON_Delete(value->value_json); - value->value_json = cJSON_CreateArray(); +// value->value_json = cJSON_CreateArray(); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - pins_ctx->saved = false; +// pins_ctx->saved = false; - return; - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// return; +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } -void pins_add_resource_route(sonic_server_request_t *req) { - char *shoggoth_id = sonic_get_path_segment(req, "shoggoth_id"); - char *label = sonic_get_path_segment(req, "label"); +// void pins_add_resource_route(sonic_server_request_t *req) { +// char *shoggoth_id = sonic_get_path_segment(req, "shoggoth_id"); +// char *label = sonic_get_path_segment(req, "label"); - result_t res = db_get_value(pins_ctx, "pins"); +// result_t res = db_get_value(pins_ctx, "pins"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); - cJSON *pin_json = cJSON_CreateObject(); +// cJSON *pin_json = cJSON_CreateObject(); - cJSON_AddStringToObject(pin_json, "shoggoth_id", shoggoth_id); - cJSON_AddStringToObject(pin_json, "label", label); +// cJSON_AddStringToObject(pin_json, "shoggoth_id", shoggoth_id); +// cJSON_AddStringToObject(pin_json, "label", label); - cJSON_AddItemToArray(value->value_json, pin_json); +// cJSON_AddItemToArray(value->value_json, pin_json); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - char *body = "OK"; - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// char *body = "OK"; +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_free_server_response(resp); - pins_ctx->saved = false; +// pins_ctx->saved = false; - return; - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// return; +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); - return; - } -} +// return; +// } +// } -void get_pins_route(sonic_server_request_t *req) { - result_t res = db_get_value(pins_ctx, "pins"); +// void get_pins_route(sonic_server_request_t *req) { +// result_t res = db_get_value(pins_ctx, "pins"); - if (is_ok(res)) { - db_value_t *value = VALUE(res); +// if (is_ok(res)) { +// db_value_t *value = VALUE(res); - if (value->value_type == VALUE_JSON) { - char *str = cJSON_Print(value->value_json); +// if (value->value_type == VALUE_JSON) { +// char *str = cJSON_Print(value->value_json); - char *body = malloc((strlen(str) + 10) * sizeof(char)); - sprintf(body, "%s %s", value_type_to_str(VALUE_JSON), str); +// char *body = malloc((strlen(str) + 10) * sizeof(char)); +// sprintf(body, "%s %s", value_type_to_str(VALUE_JSON), str); - sonic_server_response_t *resp = - sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); - sonic_response_set_body(resp, body, strlen(body)); - sonic_send_response(req, resp); +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_200, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, body, strlen(body)); +// sonic_send_response(req, resp); - free(str); - free(body); +// free(str); +// free(body); - sonic_free_server_response(resp); - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_free_server_response(resp); +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR pins is not a JSON type"); +// char err[256]; +// sprintf(err, "ERR pins is not a JSON type"); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } - } else { - sonic_server_response_t *resp = - sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } else { +// sonic_server_response_t *resp = +// sonic_new_response(STATUS_406, MIME_TEXT_PLAIN); - char err[256]; - sprintf(err, "ERR %s", res.error_message); +// char err[256]; +// sprintf(err, "ERR %s", res.error_message); - sonic_response_set_body(resp, err, strlen(err)); - sonic_send_response(req, resp); - sonic_free_server_response(resp); - } -} +// sonic_response_set_body(resp, err, strlen(err)); +// sonic_send_response(req, resp); +// sonic_free_server_response(resp); +// } +// } void add_pins_routes(sonic_server_t *server) { - sonic_add_route(server, "/pins/get_pins", METHOD_GET, get_pins_route); - sonic_add_route(server, "/pins/get_pin_label/{shoggoth_id}", METHOD_GET, - get_pin_label_route); - sonic_add_route(server, "/pins/add_resource/{shoggoth_id}/{label}", - METHOD_GET, pins_add_resource_route); - sonic_add_route(server, "/pins/remove_resource", METHOD_GET, - pins_remove_resource_route); - sonic_add_route(server, "/pins/clear", METHOD_GET, pins_clear_route); + // sonic_add_route(server, "/pins/get_pins", METHOD_GET, get_pins_route); + // sonic_add_route(server, "/pins/get_pin_label/{shoggoth_id}", METHOD_GET, + // get_pin_label_route); + // sonic_add_route(server, "/pins/add_resource/{shoggoth_id}/{label}", + // METHOD_GET, pins_add_resource_route); + // sonic_add_route(server, "/pins/remove_resource", METHOD_GET, + // pins_remove_resource_route); + // sonic_add_route(server, "/pins/clear", METHOD_GET, pins_clear_route); } result_t setup_pins(db_ctx_t *ctx) { pins_ctx = ctx; - cJSON *value_json = cJSON_CreateArray(); + // cJSON *value_json = cJSON_CreateArray(); - db_add_json_value(ctx, "pins", value_json); + // db_add_json_value(ctx, "pins", value_json); - cJSON_Delete(value_json); + // cJSON_Delete(value_json); return OK(NULL); }