From e510ed27741971a71b25e09e5e9c19b0b37a7c88 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 23 Feb 2024 16:50:55 -0500 Subject: [PATCH] Release candidate (3.7.0) (#2136) * Release candidate (3.7.0) * some extra comments. --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +- .vscode/settings.json | 5 +- CMakeLists.txt | 6 +- CONTRIBUTING.md | 3 +- Doxyfile | 2 +- doc/tape.md | 2 +- fuzz/build_fuzzer_variants.sh | 2 +- include/simdjson/dom/element.h | 5 +- include/simdjson/generic/numberparsing.h | 4 +- include/simdjson/generic/ondemand/field-inl.h | 2 +- .../simdjson/generic/ondemand/json_iterator.h | 6 +- .../generic/ondemand/token_iterator.h | 2 +- include/simdjson/simdjson.h | 2 +- include/simdjson/simdjson_version.h | 6 +- singleheader/simdjson.cpp | 81 +- singleheader/simdjson.h | 1846 ++++++++++++++++- src/generic/stage1/json_structural_indexer.h | 2 +- 17 files changed, 1871 insertions(+), 111 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7a9dbaa2d..5efc3016d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -45,12 +45,16 @@ It is fine to report bugs against our main branch, but if that is what you are d **Configuration (please complete the following information if relevant)** - OS: [e.g. Ubuntu 16.04.6 LTS] - - Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0] + - Compiler* [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0] - Version [e.g. 22] - Optimization setting (e.g., -O3) We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug. +* We do not support unreleased or experimental compilers. If you encounter an issue with a +pre-release version of a compiler, do not report it as a bug to simdjson. However, we always +invite contributions either in the form an analysis or of a code contribution. + **Indicate whether you are willing or able to provide a bug fix as a pull request** If you plan to contribute to simdjson, please read our guide: diff --git a/.vscode/settings.json b/.vscode/settings.json index ed569deec..0744e9d78 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,6 +100,9 @@ "ranges": "cpp", "span": "cpp", "__verbose_abort": "cpp", - "charconv": "cpp" + "charconv": "cpp", + "source_location": "cpp", + "strstream": "cpp", + "typeindex": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 994f7885f..48ef540bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14) project( simdjson # The version number is modified by tools/release.py - VERSION 3.6.4 + VERSION 3.7.0 DESCRIPTION "Parsing gigabytes of JSON per second" HOMEPAGE_URL "https://simdjson.org/" LANGUAGES CXX C @@ -20,8 +20,8 @@ string( # ---- Options, variables ---- # These version numbers are modified by tools/release.py -set(SIMDJSON_LIB_VERSION "19.0.0" CACHE STRING "simdjson library version") -set(SIMDJSON_LIB_SOVERSION "19" CACHE STRING "simdjson library soversion") +set(SIMDJSON_LIB_VERSION "20.0.0" CACHE STRING "simdjson library version") +set(SIMDJSON_LIB_SOVERSION "20" CACHE STRING "simdjson library soversion") option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df9c53a62..a6b70a0bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,10 +52,11 @@ General Guidelines Contributors are encouraged to : - Document their changes. Though we do not enforce a rule regarding code comments, we prefer that non-trivial algorithms and techniques be somewhat documented in the code. -- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency. +- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency. We avoid contractions (isn't, aren't) in the comments. - Modify as few lines of code as possible when working on an issue. The more lines you modify, the harder it is for your fellow human beings to understand what is going on. - Tools may report "problems" with the code, but we never delegate programming to tools: if there is a problem with the code, we need to understand it. Thus we will not "fix" code merely to please a static analyzer. - Provide tests for any new feature. We will not merge a new feature without tests. +- Run before/after benchmarks so that we can appreciate the effect of the changes on the performance. Pull Requests -------------- diff --git a/Doxyfile b/Doxyfile index f612d543d..b19184000 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = simdjson # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "3.6.4" +PROJECT_NUMBER = "3.7.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/tape.md b/doc/tape.md index 71af09b27..934a871b1 100644 --- a/doc/tape.md +++ b/doc/tape.md @@ -29,7 +29,7 @@ It is sometimes useful to start with an example. Consider the following JSON doc The following is a dump of the content of the tape, with the first number of each line representing the index of a tape element. ### The Tape -| index | element (64 bit word) | +| index | element (64-bit word) | | ----- | ------------------------------------------------------------------- | | 0 | r // pointing to 39 (right after last node) | | 1 | { // pointing to next tape location 38 (first node after the scope) | diff --git a/fuzz/build_fuzzer_variants.sh b/fuzz/build_fuzzer_variants.sh index 7aa9e87e0..4d46b5406 100755 --- a/fuzz/build_fuzzer_variants.sh +++ b/fuzz/build_fuzzer_variants.sh @@ -78,7 +78,7 @@ fi # Different levels may uncover different types of bugs, see this interesting # thread: https://github.com/google/oss-fuzz/issues/2295#issuecomment-481493392 # Oss-fuzz uses -O1 so it may be relevant to use something else than that, -# to do something oss-fuzz doesn't. +# to do something oss-fuzz does not do. variant=sanitizers-O3 if [ ! -d build-$variant ] ; then diff --git a/include/simdjson/dom/element.h b/include/simdjson/dom/element.h index dbd2dfa55..732b89e41 100644 --- a/include/simdjson/dom/element.h +++ b/include/simdjson/dom/element.h @@ -293,7 +293,7 @@ public: * * @return The integer value. * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative + * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits or is negative */ inline operator uint64_t() const noexcept(false); /** @@ -301,7 +301,7 @@ public: * * @return The integer value. * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits + * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits */ inline operator int64_t() const noexcept(false); /** @@ -309,7 +309,6 @@ public: * * @return The double value. * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a number - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative */ inline operator double() const noexcept(false); /** diff --git a/include/simdjson/generic/numberparsing.h b/include/simdjson/generic/numberparsing.h index b6f50ae77..2b119cfef 100644 --- a/include/simdjson/generic/numberparsing.h +++ b/include/simdjson/generic/numberparsing.h @@ -412,7 +412,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -609,7 +609,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { diff --git a/include/simdjson/generic/ondemand/field-inl.h b/include/simdjson/generic/ondemand/field-inl.h index 061f1860d..25021e0b2 100644 --- a/include/simdjson/generic/ondemand/field-inl.h +++ b/include/simdjson/generic/ondemand/field-inl.h @@ -12,7 +12,7 @@ namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept diff --git a/include/simdjson/generic/ondemand/json_iterator.h b/include/simdjson/generic/ondemand/json_iterator.h index 36335dbfd..11003e34c 100644 --- a/include/simdjson/generic/ondemand/json_iterator.h +++ b/include/simdjson/generic/ondemand/json_iterator.h @@ -146,7 +146,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -174,7 +174,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -191,7 +191,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; diff --git a/include/simdjson/generic/ondemand/token_iterator.h b/include/simdjson/generic/ondemand/token_iterator.h index 968281f2f..71079ab34 100644 --- a/include/simdjson/generic/ondemand/token_iterator.h +++ b/include/simdjson/generic/ondemand/token_iterator.h @@ -47,7 +47,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** diff --git a/include/simdjson/simdjson.h b/include/simdjson/simdjson.h index 3fe721122..551673aef 100644 --- a/include/simdjson/simdjson.h +++ b/include/simdjson/simdjson.h @@ -1,6 +1,6 @@ /** * @file - * @deprecated We'll be removing this file so it isn't confused with the top level simdjson.h + * @deprecated We will be removing this file so it is not confused with the top level simdjson.h */ #ifndef SIMDJSON_SIMDJSON_H #define SIMDJSON_SIMDJSON_H diff --git a/include/simdjson/simdjson_version.h b/include/simdjson/simdjson_version.h index 7fb876f52..4842dcfa4 100644 --- a/include/simdjson/simdjson_version.h +++ b/include/simdjson/simdjson_version.h @@ -4,7 +4,7 @@ #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.6.4" +#define SIMDJSON_VERSION "3.7.0" namespace simdjson { enum { @@ -15,11 +15,11 @@ enum { /** * The minor version (major.MINOR.revision) of simdjson being used. */ - SIMDJSON_VERSION_MINOR = 6, + SIMDJSON_VERSION_MINOR = 7, /** * The revision (major.minor.REVISION) of simdjson being used. */ - SIMDJSON_VERSION_REVISION = 4 + SIMDJSON_VERSION_REVISION = 0 }; } // namespace simdjson diff --git a/singleheader/simdjson.cpp b/singleheader/simdjson.cpp index 358edaf61..5511f3d03 100644 --- a/singleheader/simdjson.cpp +++ b/singleheader/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2024-02-04 22:03:23 -0500. Do not edit! */ +/* auto-generated on 2024-02-18 11:50:12 -0500. Do not edit! */ /* including simdjson.cpp: */ /* begin file simdjson.cpp */ #define SIMDJSON_SRC_SIMDJSON_CPP @@ -7134,6 +7134,38 @@ static const simdjson::westmere::implementation* get_westmere_singleton() { namespace simdjson { namespace internal { +// When there is a single implementation, we should not pay a price +// for dispatching to the best implementation. We should just use the +// one we have. This is a compile-time check. +#define SIMDJSON_SINGLE_IMPLEMENTATION (SIMDJSON_IMPLEMENTATION_ICELAKE \ + + SIMDJSON_IMPLEMENTATION_HASWELL + SIMDJSON_IMPLEMENTATION_WESTMERE \ + + SIMDJSON_IMPLEMENTATION_ARM64 + SIMDJSON_IMPLEMENTATION_PPC64 \ + + SIMDJSON_IMPLEMENTATION_FALLBACK == 1) + +#if SIMDJSON_SINGLE_IMPLEMENTATION + static const implementation* get_single_implementation() { + return +#if SIMDJSON_IMPLEMENTATION_ICELAKE + get_icelake_singleton(); +#endif +#if SIMDJSON_IMPLEMENTATION_HASWELL + get_haswell_singleton(); +#endif +#if SIMDJSON_IMPLEMENTATION_WESTMERE + get_westmere_singleton(); +#endif +#if SIMDJSON_IMPLEMENTATION_ARM64 + get_arm64_singleton(); +#endif +#if SIMDJSON_IMPLEMENTATION_PPC64 + get_ppc64_singleton(); +#endif +#if SIMDJSON_IMPLEMENTATION_FALLBACK + get_fallback_singleton(); +#endif +} +#endif + // Static array of known implementations. We're hoping these get baked into the executable // without requiring a static initializer. @@ -7263,9 +7295,16 @@ SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_avai } SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { - static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; - static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; - return active_implementation; +#if SIMDJSON_SINGLE_IMPLEMENTATION + // We immediately select the only implementation we have, skipping the + // detect_best_supported_implementation_on_first_use_singleton. + static internal::atomic_ptr active_implementation{internal::get_single_implementation()}; + return active_implementation; +#else + static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; + static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; + return active_implementation; +#endif } simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept { @@ -9014,7 +9053,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -9211,7 +9250,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -12115,7 +12154,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { // up enough CPU: the second half of the functions is highly serial, only using 1 execution core // at a time. The second input's scans has some dependency on the first ones finishing it, but // they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that // to finish: utf-8 checks and generating the output from the last iteration. // // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all @@ -15302,7 +15341,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -15499,7 +15538,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -18280,7 +18319,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { // up enough CPU: the second half of the functions is highly serial, only using 1 execution core // at a time. The second input's scans has some dependency on the first ones finishing it, but // they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that // to finish: utf-8 checks and generating the output from the last iteration. // // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all @@ -21462,7 +21501,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -21659,7 +21698,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -24438,7 +24477,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { // up enough CPU: the second half of the functions is highly serial, only using 1 execution core // at a time. The second input's scans has some dependency on the first ones finishing it, but // they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that // to finish: utf-8 checks and generating the output from the last iteration. // // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all @@ -27778,7 +27817,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -27975,7 +28014,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -30867,7 +30906,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { // up enough CPU: the second half of the functions is highly serial, only using 1 execution core // at a time. The second input's scans has some dependency on the first ones finishing it, but // they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that // to finish: utf-8 checks and generating the output from the last iteration. // // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all @@ -34460,7 +34499,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -34657,7 +34696,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -37870,7 +37909,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { // up enough CPU: the second half of the functions is highly serial, only using 1 execution core // at a time. The second input's scans has some dependency on the first ones finishing it, but // they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that // to finish: utf-8 checks and generating the output from the last iteration. // // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all @@ -40569,7 +40608,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -40766,7 +40805,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { diff --git a/singleheader/simdjson.h b/singleheader/simdjson.h index 4194dd64f..96a5fae0f 100644 --- a/singleheader/simdjson.h +++ b/singleheader/simdjson.h @@ -1,4 +1,4 @@ -/* auto-generated on 2024-02-04 22:03:23 -0500. Do not edit! */ +/* auto-generated on 2024-02-18 11:50:12 -0500. Do not edit! */ /* including simdjson.h: */ /* begin file simdjson.h */ #ifndef SIMDJSON_H @@ -2344,7 +2344,7 @@ namespace std { #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.6.4" +#define SIMDJSON_VERSION "3.7.0" namespace simdjson { enum { @@ -2355,11 +2355,11 @@ enum { /** * The minor version (major.MINOR.revision) of simdjson being used. */ - SIMDJSON_VERSION_MINOR = 6, + SIMDJSON_VERSION_MINOR = 7, /** * The revision (major.minor.REVISION) of simdjson being used. */ - SIMDJSON_VERSION_REVISION = 4 + SIMDJSON_VERSION_REVISION = 0 }; } // namespace simdjson @@ -3505,6 +3505,9 @@ struct padded_string final { * @param length the number of bytes to copy */ explicit inline padded_string(const char *data, size_t length) noexcept; +#ifdef __cpp_char8_t + explicit inline padded_string(const char8_t *data, size_t length) noexcept; +#endif /** * Create a new padded string by copying the given input. * @@ -3616,6 +3619,9 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result(s), len, capacity) { } - +#ifdef __cpp_char8_t +inline padded_string_view::padded_string_view(const char8_t* s, size_t len, size_t capacity) noexcept + : padded_string_view(reinterpret_cast(s), len, capacity) +{ +} +#endif inline padded_string_view::padded_string_view(const std::string &s) noexcept : std::string_view(s), _capacity(s.capacity()) { @@ -3785,6 +3801,7 @@ inline padded_string_view::padded_string_view(const std::string &s) noexcept inline padded_string_view::padded_string_view(std::string_view s, size_t capacity) noexcept : std::string_view(s), _capacity(capacity) { + if(_capacity < s.length()) { _capacity = s.length(); } } inline size_t padded_string_view::capacity() const noexcept { return _capacity; } @@ -3858,6 +3875,14 @@ inline padded_string::padded_string(const char *data, size_t length) noexcept std::memcpy(data_ptr, data, length); } } +#ifdef __cpp_char8_t +inline padded_string::padded_string(const char8_t *data, size_t length) noexcept + : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) { + if ((data != nullptr) && (data_ptr != nullptr)) { + std::memcpy(data_ptr, reinterpret_cast(data), length); + } +} +#endif // note: do not pass std::string arguments by value inline padded_string::padded_string(const std::string & str_ ) noexcept : viable_size(str_.size()), data_ptr(internal::allocate_padded_buffer(str_.size())) { @@ -3977,7 +4002,11 @@ inline simdjson_result padded_string::load(std::string_view filen inline simdjson::padded_string operator "" _padded(const char *str, size_t len) { return simdjson::padded_string(str, len); } - +#ifdef __cpp_char8_t +inline simdjson::padded_string operator "" _padded(const char8_t *str, size_t len) { + return simdjson::padded_string(reinterpret_cast(str), len); +} +#endif #endif // SIMDJSON_PADDED_STRING_INL_H /* end file simdjson/padded_string-inl.h */ /* skipped duplicate #include "simdjson/padded_string_view.h" */ @@ -5636,7 +5665,7 @@ public: * * @return The integer value. * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative + * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits or is negative */ inline operator uint64_t() const noexcept(false); /** @@ -5644,7 +5673,7 @@ public: * * @return The integer value. * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits + * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits */ inline operator int64_t() const noexcept(false); /** @@ -5652,7 +5681,6 @@ public: * * @return The double value. * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a number - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative */ inline operator double() const noexcept(false); /** @@ -11227,7 +11255,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -11424,7 +11452,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -13276,7 +13304,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -13473,7 +13501,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -15817,7 +15845,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -16014,7 +16042,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -18357,7 +18385,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -18554,7 +18582,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -21012,7 +21040,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -21209,7 +21237,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -23990,7 +24018,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s // If there were more than 18 digits, we may have overflowed the integer. We have to do // something!!!! if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow + // Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow while (*start_exp == '0') { start_exp++; } // 19 digits could overflow int64_t and is kind of absurd anyway. We don't // support exponents smaller than -999,999,999,999,999,999 and bigger @@ -24187,7 +24215,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } } - // Write unsigned if it doesn't fit in a signed integer. + // Write unsigned if it does not fit in a signed integer. if (i > uint64_t(INT64_MAX)) { WRITE_UNSIGNED(i, src, writer); } else { @@ -26344,6 +26372,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand @@ -26769,6 +26798,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -26944,6 +26980,20 @@ public: */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + protected: /** * Create a value. @@ -26983,6 +27033,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -27097,6 +27148,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; @@ -27111,6 +27163,7 @@ public: /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; } // namespace simdjson @@ -27229,7 +27282,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -27482,7 +27535,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -27510,7 +27563,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -27527,7 +27580,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; @@ -27636,6 +27689,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; template @@ -28500,6 +28554,22 @@ public: * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the array and returns a string_view instance corresponding to the * array as represented in JSON. It points inside the original document. @@ -28590,6 +28660,7 @@ public: inline simdjson_result reset() & noexcept; simdjson_inline simdjson_result at(size_t index) noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; }; @@ -29133,6 +29204,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -29280,6 +29359,14 @@ public: * auto doc = parser.iterate(json); * doc.at_pointer("//a/1") == 20 * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/\\u00E9") == 123 + * doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD) + * * Note that at_pointer() automatically calls rewind between each call. Thus * all values, objects and arrays that you have created so far (including unescaped strings) * are invalidated. After calling at_pointer, you need to consume the result: string values @@ -29296,6 +29383,30 @@ public: * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_path(".\\u00E9") == 123 + * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the document and returns a string_view instance corresponding to the * document as represented in JSON. It points inside the original byte array containing @@ -29391,6 +29502,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -29400,6 +29512,8 @@ public: simdjson_inline simdjson_result get_number() noexcept; simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + private: document *doc{nullptr}; }; @@ -29465,6 +29579,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -29476,6 +29591,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -29534,6 +29650,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; @@ -29544,6 +29661,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -29937,6 +30055,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -29969,6 +30092,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; @@ -30120,6 +30244,19 @@ public: */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Reset the iterator so that we are pointing back at the * beginning of the object. You should still consume values only once even if you @@ -30207,6 +30344,8 @@ public: simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; @@ -30576,6 +30715,59 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + if (json_path.empty() || (json_path.front() != '.' && + json_path.front() != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + size_t i = 0; + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} + +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { @@ -30630,6 +30822,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); @@ -30730,6 +30926,8 @@ simdjson_inline simdjson_result &simdjson_resul /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -30990,6 +31188,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -31030,6 +31235,23 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso } } +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -31205,6 +31427,10 @@ simdjson_inline simdjson_result simdjson_result return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -31293,6 +31519,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} } // namespace simdjson @@ -31369,6 +31599,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } @@ -31377,6 +31608,7 @@ simdjson_inline simdjson_result document_reference::get_number_type simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } @@ -31508,6 +31740,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -31578,6 +31814,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} } // namespace simdjson @@ -32026,7 +32268,7 @@ namespace simdjson { namespace arm64 { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept @@ -32057,6 +32299,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -32090,10 +32338,17 @@ simdjson_inline simdjson_result simdjson_resul if (error()) { return error(); } return first.key(); } + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); @@ -33032,6 +33287,14 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result object::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { + return INVALID_JSON_POINTER; + } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result object::count_fields() & noexcept { size_t count{0}; // Important: we do not consume any of the values. @@ -33104,6 +33367,14 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } return first.reset(); @@ -33994,6 +34265,8 @@ simdjson_inline simdjson_result::simdjson_resul /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -34171,6 +34444,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -34231,6 +34512,19 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -34384,6 +34678,10 @@ simdjson_inline simdjson_result simdjson_result::i if (error()) { return error(); } return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -34460,11 +34758,22 @@ simdjson_inline simdjson_result simdjson_result return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H @@ -36299,6 +36608,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand @@ -36724,6 +37034,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -36899,6 +37216,20 @@ public: */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + protected: /** * Create a value. @@ -36938,6 +37269,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -37052,6 +37384,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; @@ -37066,6 +37399,7 @@ public: /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; } // namespace simdjson @@ -37184,7 +37518,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -37437,7 +37771,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -37465,7 +37799,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -37482,7 +37816,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; @@ -37591,6 +37925,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; template @@ -38455,6 +38790,22 @@ public: * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the array and returns a string_view instance corresponding to the * array as represented in JSON. It points inside the original document. @@ -38545,6 +38896,7 @@ public: inline simdjson_result reset() & noexcept; simdjson_inline simdjson_result at(size_t index) noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; }; @@ -39088,6 +39440,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -39235,6 +39595,14 @@ public: * auto doc = parser.iterate(json); * doc.at_pointer("//a/1") == 20 * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/\\u00E9") == 123 + * doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD) + * * Note that at_pointer() automatically calls rewind between each call. Thus * all values, objects and arrays that you have created so far (including unescaped strings) * are invalidated. After calling at_pointer, you need to consume the result: string values @@ -39251,6 +39619,30 @@ public: * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_path(".\\u00E9") == 123 + * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the document and returns a string_view instance corresponding to the * document as represented in JSON. It points inside the original byte array containing @@ -39346,6 +39738,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -39355,6 +39748,8 @@ public: simdjson_inline simdjson_result get_number() noexcept; simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + private: document *doc{nullptr}; }; @@ -39420,6 +39815,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -39431,6 +39827,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -39489,6 +39886,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; @@ -39499,6 +39897,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -39892,6 +40291,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -39924,6 +40328,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; @@ -40075,6 +40480,19 @@ public: */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Reset the iterator so that we are pointing back at the * beginning of the object. You should still consume values only once even if you @@ -40162,6 +40580,8 @@ public: simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; @@ -40531,6 +40951,59 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + if (json_path.empty() || (json_path.front() != '.' && + json_path.front() != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + size_t i = 0; + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} + +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { @@ -40585,6 +41058,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); @@ -40685,6 +41162,8 @@ simdjson_inline simdjson_result &simdjson_re /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -40945,6 +41424,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -40985,6 +41471,23 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso } } +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -41160,6 +41663,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -41248,6 +41755,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} } // namespace simdjson @@ -41324,6 +41835,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } @@ -41332,6 +41844,7 @@ simdjson_inline simdjson_result document_reference::get_number_type simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } @@ -41463,6 +41976,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -41533,6 +42050,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} } // namespace simdjson @@ -41981,7 +42504,7 @@ namespace simdjson { namespace fallback { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept @@ -42012,6 +42535,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -42045,10 +42574,17 @@ simdjson_inline simdjson_result simdjson_re if (error()) { return error(); } return first.key(); } + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); @@ -42987,6 +43523,14 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result object::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { + return INVALID_JSON_POINTER; + } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result object::count_fields() & noexcept { size_t count{0}; // Important: we do not consume any of the values. @@ -43059,6 +43603,14 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } return first.reset(); @@ -43949,6 +44501,8 @@ simdjson_inline simdjson_result::simdjson_re /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -44126,6 +44680,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -44186,6 +44748,19 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -44339,6 +44914,10 @@ simdjson_inline simdjson_result simdjson_result if (error()) { return error(); } return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -44415,11 +44994,22 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H @@ -46746,6 +47336,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand @@ -47171,6 +47762,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -47346,6 +47944,20 @@ public: */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + protected: /** * Create a value. @@ -47385,6 +47997,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -47499,6 +48112,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; @@ -47513,6 +48127,7 @@ public: /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; } // namespace simdjson @@ -47631,7 +48246,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -47884,7 +48499,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -47912,7 +48527,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -47929,7 +48544,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; @@ -48038,6 +48653,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; template @@ -48902,6 +49518,22 @@ public: * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the array and returns a string_view instance corresponding to the * array as represented in JSON. It points inside the original document. @@ -48992,6 +49624,7 @@ public: inline simdjson_result reset() & noexcept; simdjson_inline simdjson_result at(size_t index) noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; }; @@ -49535,6 +50168,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -49682,6 +50323,14 @@ public: * auto doc = parser.iterate(json); * doc.at_pointer("//a/1") == 20 * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/\\u00E9") == 123 + * doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD) + * * Note that at_pointer() automatically calls rewind between each call. Thus * all values, objects and arrays that you have created so far (including unescaped strings) * are invalidated. After calling at_pointer, you need to consume the result: string values @@ -49698,6 +50347,30 @@ public: * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_path(".\\u00E9") == 123 + * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the document and returns a string_view instance corresponding to the * document as represented in JSON. It points inside the original byte array containing @@ -49793,6 +50466,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -49802,6 +50476,8 @@ public: simdjson_inline simdjson_result get_number() noexcept; simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + private: document *doc{nullptr}; }; @@ -49867,6 +50543,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -49878,6 +50555,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -49936,6 +50614,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; @@ -49946,6 +50625,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -50339,6 +51019,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -50371,6 +51056,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; @@ -50522,6 +51208,19 @@ public: */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Reset the iterator so that we are pointing back at the * beginning of the object. You should still consume values only once even if you @@ -50609,6 +51308,8 @@ public: simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; @@ -50978,6 +51679,59 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + if (json_path.empty() || (json_path.front() != '.' && + json_path.front() != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + size_t i = 0; + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} + +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { @@ -51032,6 +51786,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); @@ -51132,6 +51890,8 @@ simdjson_inline simdjson_result &simdjson_res /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -51392,6 +52152,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -51432,6 +52199,23 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso } } +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -51607,6 +52391,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -51695,6 +52483,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} } // namespace simdjson @@ -51771,6 +52563,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } @@ -51779,6 +52572,7 @@ simdjson_inline simdjson_result document_reference::get_number_type simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } @@ -51910,6 +52704,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -51980,6 +52778,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} } // namespace simdjson @@ -52428,7 +53232,7 @@ namespace simdjson { namespace haswell { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept @@ -52459,6 +53263,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -52492,10 +53302,17 @@ simdjson_inline simdjson_result simdjson_res if (error()) { return error(); } return first.key(); } + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); @@ -53434,6 +54251,14 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result object::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { + return INVALID_JSON_POINTER; + } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result object::count_fields() & noexcept { size_t count{0}; // Important: we do not consume any of the values. @@ -53506,6 +54331,14 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } return first.reset(); @@ -54396,6 +55229,8 @@ simdjson_inline simdjson_result::simdjson_res /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -54573,6 +55408,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -54633,6 +55476,19 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -54786,6 +55642,10 @@ simdjson_inline simdjson_result simdjson_result: if (error()) { return error(); } return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -54862,11 +55722,22 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H @@ -57192,6 +58063,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand @@ -57617,6 +58489,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -57792,6 +58671,20 @@ public: */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + protected: /** * Create a value. @@ -57831,6 +58724,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -57945,6 +58839,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; @@ -57959,6 +58854,7 @@ public: /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; } // namespace simdjson @@ -58077,7 +58973,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -58330,7 +59226,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -58358,7 +59254,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -58375,7 +59271,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; @@ -58484,6 +59380,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; template @@ -59348,6 +60245,22 @@ public: * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the array and returns a string_view instance corresponding to the * array as represented in JSON. It points inside the original document. @@ -59438,6 +60351,7 @@ public: inline simdjson_result reset() & noexcept; simdjson_inline simdjson_result at(size_t index) noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; }; @@ -59981,6 +60895,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -60128,6 +61050,14 @@ public: * auto doc = parser.iterate(json); * doc.at_pointer("//a/1") == 20 * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/\\u00E9") == 123 + * doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD) + * * Note that at_pointer() automatically calls rewind between each call. Thus * all values, objects and arrays that you have created so far (including unescaped strings) * are invalidated. After calling at_pointer, you need to consume the result: string values @@ -60144,6 +61074,30 @@ public: * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_path(".\\u00E9") == 123 + * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the document and returns a string_view instance corresponding to the * document as represented in JSON. It points inside the original byte array containing @@ -60239,6 +61193,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -60248,6 +61203,8 @@ public: simdjson_inline simdjson_result get_number() noexcept; simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + private: document *doc{nullptr}; }; @@ -60313,6 +61270,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -60324,6 +61282,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -60382,6 +61341,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; @@ -60392,6 +61352,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -60785,6 +61746,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -60817,6 +61783,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; @@ -60968,6 +61935,19 @@ public: */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Reset the iterator so that we are pointing back at the * beginning of the object. You should still consume values only once even if you @@ -61055,6 +62035,8 @@ public: simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; @@ -61424,6 +62406,59 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + if (json_path.empty() || (json_path.front() != '.' && + json_path.front() != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + size_t i = 0; + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} + +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { @@ -61478,6 +62513,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); @@ -61578,6 +62617,8 @@ simdjson_inline simdjson_result &simdjson_res /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -61838,6 +62879,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -61878,6 +62926,23 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso } } +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -62053,6 +63118,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -62141,6 +63210,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} } // namespace simdjson @@ -62217,6 +63290,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } @@ -62225,6 +63299,7 @@ simdjson_inline simdjson_result document_reference::get_number_type simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } @@ -62356,6 +63431,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -62426,6 +63505,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} } // namespace simdjson @@ -62874,7 +63959,7 @@ namespace simdjson { namespace icelake { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept @@ -62905,6 +63990,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -62938,10 +64029,17 @@ simdjson_inline simdjson_result simdjson_res if (error()) { return error(); } return first.key(); } + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); @@ -63880,6 +64978,14 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result object::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { + return INVALID_JSON_POINTER; + } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result object::count_fields() & noexcept { size_t count{0}; // Important: we do not consume any of the values. @@ -63952,6 +65058,14 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } return first.reset(); @@ -64842,6 +65956,8 @@ simdjson_inline simdjson_result::simdjson_res /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -65019,6 +66135,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -65079,6 +66203,19 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -65232,6 +66369,10 @@ simdjson_inline simdjson_result simdjson_result: if (error()) { return error(); } return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -65308,11 +66449,22 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H @@ -67753,6 +68905,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand @@ -68178,6 +69331,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -68353,6 +69513,20 @@ public: */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + protected: /** * Create a value. @@ -68392,6 +69566,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -68506,6 +69681,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; @@ -68520,6 +69696,7 @@ public: /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; } // namespace simdjson @@ -68638,7 +69815,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -68891,7 +70068,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -68919,7 +70096,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -68936,7 +70113,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; @@ -69045,6 +70222,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; template @@ -69909,6 +71087,22 @@ public: * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the array and returns a string_view instance corresponding to the * array as represented in JSON. It points inside the original document. @@ -69999,6 +71193,7 @@ public: inline simdjson_result reset() & noexcept; simdjson_inline simdjson_result at(size_t index) noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; }; @@ -70542,6 +71737,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -70689,6 +71892,14 @@ public: * auto doc = parser.iterate(json); * doc.at_pointer("//a/1") == 20 * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/\\u00E9") == 123 + * doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD) + * * Note that at_pointer() automatically calls rewind between each call. Thus * all values, objects and arrays that you have created so far (including unescaped strings) * are invalidated. After calling at_pointer, you need to consume the result: string values @@ -70705,6 +71916,30 @@ public: * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_path(".\\u00E9") == 123 + * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the document and returns a string_view instance corresponding to the * document as represented in JSON. It points inside the original byte array containing @@ -70800,6 +72035,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -70809,6 +72045,8 @@ public: simdjson_inline simdjson_result get_number() noexcept; simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + private: document *doc{nullptr}; }; @@ -70874,6 +72112,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -70885,6 +72124,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -70943,6 +72183,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; @@ -70953,6 +72194,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -71346,6 +72588,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -71378,6 +72625,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; @@ -71529,6 +72777,19 @@ public: */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Reset the iterator so that we are pointing back at the * beginning of the object. You should still consume values only once even if you @@ -71616,6 +72877,8 @@ public: simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; @@ -71985,6 +73248,59 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + if (json_path.empty() || (json_path.front() != '.' && + json_path.front() != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + size_t i = 0; + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} + +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { @@ -72039,6 +73355,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); @@ -72139,6 +73459,8 @@ simdjson_inline simdjson_result &simdjson_resul /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -72399,6 +73721,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -72439,6 +73768,23 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso } } +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -72614,6 +73960,10 @@ simdjson_inline simdjson_result simdjson_result return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -72702,6 +74052,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} } // namespace simdjson @@ -72778,6 +74132,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } @@ -72786,6 +74141,7 @@ simdjson_inline simdjson_result document_reference::get_number_type simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } @@ -72917,6 +74273,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -72987,6 +74347,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} } // namespace simdjson @@ -73435,7 +74801,7 @@ namespace simdjson { namespace ppc64 { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept @@ -73466,6 +74832,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -73499,10 +74871,17 @@ simdjson_inline simdjson_result simdjson_resul if (error()) { return error(); } return first.key(); } + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); @@ -74441,6 +75820,14 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result object::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { + return INVALID_JSON_POINTER; + } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result object::count_fields() & noexcept { size_t count{0}; // Important: we do not consume any of the values. @@ -74513,6 +75900,14 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } return first.reset(); @@ -75403,6 +76798,8 @@ simdjson_inline simdjson_result::simdjson_resul /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -75580,6 +76977,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -75640,6 +77045,19 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -75793,6 +77211,10 @@ simdjson_inline simdjson_result simdjson_result::i if (error()) { return error(); } return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -75869,11 +77291,22 @@ simdjson_inline simdjson_result simdjson_result return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H @@ -78637,6 +80070,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand @@ -79062,6 +80496,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -79237,6 +80678,20 @@ public: */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + protected: /** * Create a value. @@ -79276,6 +80731,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -79390,6 +80846,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; @@ -79404,6 +80861,7 @@ public: /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; } // namespace simdjson @@ -79522,7 +80980,7 @@ public: * 1 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -79775,7 +81233,7 @@ public: * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; /** @@ -79803,7 +81261,7 @@ public: * @param position The position of the token to retrieve. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek(token_position position) const noexcept; /** @@ -79820,7 +81278,7 @@ public: * This is not null-terminated; it is a view into the JSON. * * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * it is not used ... */ simdjson_inline const uint8_t *peek_last() const noexcept; @@ -79929,6 +81387,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; template @@ -80793,6 +82252,22 @@ public: * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the array and returns a string_view instance corresponding to the * array as represented in JSON. It points inside the original document. @@ -80883,6 +82358,7 @@ public: inline simdjson_result reset() & noexcept; simdjson_inline simdjson_result at(size_t index) noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; }; @@ -81426,6 +82902,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -81573,6 +83057,14 @@ public: * auto doc = parser.iterate(json); * doc.at_pointer("//a/1") == 20 * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/\\u00E9") == 123 + * doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD) + * * Note that at_pointer() automatically calls rewind between each call. Thus * all values, objects and arrays that you have created so far (including unescaped strings) * are invalidated. After calling at_pointer, you need to consume the result: string values @@ -81589,6 +83081,30 @@ public: * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). */ simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * Key values are matched exactly, without unescaping or Unicode normalization. + * We do a byte-by-byte comparison. E.g. + * + * const padded_string json = "{\"\\u00E9\":123}"_padded; + * auto doc = parser.iterate(json); + * doc.at_path(".\\u00E9") == 123 + * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Consumes the document and returns a string_view instance corresponding to the * document as represented in JSON. It points inside the original byte array containing @@ -81684,6 +83200,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -81693,6 +83210,8 @@ public: simdjson_inline simdjson_result get_number() noexcept; simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + private: document *doc{nullptr}; }; @@ -81758,6 +83277,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -81769,6 +83289,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -81827,6 +83348,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; @@ -81837,6 +83359,7 @@ public: simdjson_inline simdjson_result raw_json_token() noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; }; @@ -82230,6 +83753,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -82262,6 +83790,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; @@ -82413,6 +83942,19 @@ public: */ inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + /** * Reset the iterator so that we are pointing back at the * beginning of the object. You should still consume values only once even if you @@ -82500,6 +84042,8 @@ public: simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; @@ -82869,6 +84413,59 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + if (json_path.empty() || (json_path.front() != '.' && + json_path.front() != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + size_t i = 0; + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} + +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { @@ -82923,6 +84520,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); @@ -83023,6 +84624,8 @@ simdjson_inline simdjson_result &simdjson_re /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -83283,6 +84886,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -83323,6 +84933,23 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso } } +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -83498,6 +85125,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -83586,6 +85217,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} } // namespace simdjson @@ -83662,6 +85297,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } @@ -83670,6 +85306,7 @@ simdjson_inline simdjson_result document_reference::get_number_type simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } @@ -83801,6 +85438,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -83871,6 +85512,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} } // namespace simdjson @@ -84319,7 +85966,7 @@ namespace simdjson { namespace westmere { namespace ondemand { -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit +// clang 6 does not think the default constructor can be noexcept, so we make it explicit simdjson_inline field::field() noexcept : std::pair() {} simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept @@ -84350,6 +85997,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -84383,10 +86036,17 @@ simdjson_inline simdjson_result simdjson_re if (error()) { return error(); } return first.key(); } + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); @@ -85325,6 +86985,14 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result object::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { + return INVALID_JSON_POINTER; + } + return at_pointer(json_pointer); +} + simdjson_inline simdjson_result object::count_fields() & noexcept { size_t count{0}; // Important: we do not consume any of the values. @@ -85397,6 +87065,14 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } return first.reset(); @@ -86287,6 +87963,8 @@ simdjson_inline simdjson_result::simdjson_re /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ @@ -86464,6 +88142,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -86524,6 +88210,19 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -86677,6 +88376,10 @@ simdjson_inline simdjson_result simdjson_result if (error()) { return error(); } return first.is_scalar(); } +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); @@ -86753,11 +88456,22 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H diff --git a/src/generic/stage1/json_structural_indexer.h b/src/generic/stage1/json_structural_indexer.h index fe00c286d..cfdedf012 100644 --- a/src/generic/stage1/json_structural_indexer.h +++ b/src/generic/stage1/json_structural_indexer.h @@ -183,7 +183,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { // up enough CPU: the second half of the functions is highly serial, only using 1 execution core // at a time. The second input's scans has some dependency on the first ones finishing it, but // they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that // to finish: utf-8 checks and generating the output from the last iteration. // // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all